Skip to content

Commit 889aee0

Browse files
committed
#354 Allows scheme and domain to match ignoring case
1 parent d4c8cff commit 889aee0

File tree

6 files changed

+110
-5
lines changed

6 files changed

+110
-5
lines changed

lib/onelogin/ruby-saml/logoutresponse.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def valid_in_response_to?
195195
def valid_issuer?
196196
return true if settings.idp_entity_id.nil? || issuer.nil?
197197

198-
unless URI.parse(issuer) == URI.parse(settings.idp_entity_id)
198+
unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
199199
return append_error("Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>")
200200
end
201201
true

lib/onelogin/ruby-saml/response.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ def validate_destination
600600

601601
return true if settings.assertion_consumer_service_url.nil? || settings.assertion_consumer_service_url.empty?
602602

603-
unless destination == settings.assertion_consumer_service_url
603+
unless OneLogin::RubySaml::Utils.uri_match?(destination, settings.assertion_consumer_service_url)
604604
error_msg = "The response was received at #{destination} instead of #{settings.assertion_consumer_service_url}"
605605
return append_error(error_msg)
606606
end
@@ -675,7 +675,7 @@ def validate_issuer
675675
end
676676

677677
obtained_issuers.each do |issuer|
678-
unless URI.parse(issuer) == URI.parse(settings.idp_entity_id)
678+
unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
679679
error_msg = "Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>"
680680
return append_error(error_msg)
681681
end

lib/onelogin/ruby-saml/slo_logoutrequest.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def validate_request_state
212212
def validate_issuer
213213
return true if settings.nil? || settings.idp_entity_id.nil? || issuer.nil?
214214

215-
unless URI.parse(issuer) == URI.parse(settings.idp_entity_id)
215+
unless OneLogin::RubySaml::Utils.uri_match?(issuer, settings.idp_entity_id)
216216
return append_error("Doesn't match the issuer, expected: <#{settings.idp_entity_id}>, but was: <#{issuer}>")
217217
end
218218

lib/onelogin/ruby-saml/utils.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,33 @@ def self.retrieve_plaintext(cipher_text, symmetric_key, algorithm)
193193
def self.uuid
194194
RUBY_VERSION < '1.9' ? "_#{@@uuid_generator.generate}" : "_#{SecureRandom.uuid}"
195195
end
196+
197+
# Given two strings, attempt to match them as URIs using Rails' parse method. If they can be parsed,
198+
# then the fully-qualified domain name and the host should performa a case-insensitive match, per the
199+
# RFC for URIs. If Rails can not parse the string in to URL pieces, return a boolean match of the
200+
# two strings. This maintains the previous functionality.
201+
# @return [Boolean]
202+
def self.uri_match?(destination_url, settings_url)
203+
dest_uri = URI.parse(destination_url)
204+
acs_uri = URI.parse(settings_url)
205+
206+
if dest_uri.scheme.nil? || acs_uri.scheme.nil? || dest_uri.host.nil? || acs_uri.host.nil?
207+
raise URI::InvalidURIError
208+
else
209+
dest_uri.scheme.downcase == acs_uri.scheme.downcase &&
210+
dest_uri.host.downcase == acs_uri.host.downcase &&
211+
dest_uri.path == acs_uri.path &&
212+
dest_uri.query == acs_uri.query
213+
end
214+
rescue URI::InvalidURIError
215+
original_uri_match?(destination_url, settings_url)
216+
end
217+
218+
# If Rails' URI.parse can't match to valid URL, default back to the original matching service.
219+
# @return [Boolean]
220+
def self.original_uri_match?(destination_url, settings_url)
221+
destination_url == settings_url
222+
end
196223
end
197224
end
198225
end

test/response_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,34 @@ class RubySamlTest < Minitest::Test
435435
assert !response_empty_destination.send(:validate_destination)
436436
assert_includes response_empty_destination.errors, "The response has an empty Destination value"
437437
end
438+
439+
it "returns true on a case insensitive match on the domain" do
440+
response_valid_signed_without_x509certificate.settings = settings
441+
response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url = 'http://APP.muDa.no/sso/consume'
442+
assert response_valid_signed_without_x509certificate.send(:validate_destination)
443+
assert_empty response_valid_signed_without_x509certificate.errors
444+
end
445+
446+
it "returns true on a case insensitive match on the scheme" do
447+
response_valid_signed_without_x509certificate.settings = settings
448+
response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url = 'HTTP://app.muda.no/sso/consume'
449+
assert response_valid_signed_without_x509certificate.send(:validate_destination)
450+
assert_empty response_valid_signed_without_x509certificate.errors
451+
end
452+
453+
it "returns false on a case insenstive match on the path" do
454+
response_valid_signed_without_x509certificate.settings = settings
455+
response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url = 'http://app.muda.no/SSO/consume'
456+
assert !response_valid_signed_without_x509certificate.send(:validate_destination)
457+
assert_includes response_valid_signed_without_x509certificate.errors, "The response was received at #{response_valid_signed_without_x509certificate.destination} instead of #{response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url}"
458+
end
459+
460+
it "returns true if it can't parse out a full URI." do
461+
response_valid_signed_without_x509certificate.settings = settings
462+
response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url = 'presenter'
463+
assert !response_valid_signed_without_x509certificate.send(:validate_destination)
464+
assert_includes response_valid_signed_without_x509certificate.errors, "The response was received at #{response_valid_signed_without_x509certificate.destination} instead of #{response_valid_signed_without_x509certificate.settings.assertion_consumer_service_url}"
465+
end
438466
end
439467

440468
describe "#validate_issuer" do

test/utils_test.rb

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require "test_helper"
1+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
22

33
class UtilsTest < Minitest::Test
44
describe ".format_cert" do
@@ -154,5 +154,55 @@ class UtilsTest < Minitest::Test
154154
refute_equal OneLogin::RubySaml::Utils.uuid, OneLogin::RubySaml::Utils.uuid
155155
end
156156
end
157+
158+
describe 'uri_match' do
159+
it 'matches two urls' do
160+
destination = 'http://www.example.com/test?var=stuff'
161+
settings = 'http://www.example.com/test?var=stuff'
162+
assert OneLogin::RubySaml::Utils.uri_match?(destination, settings)
163+
end
164+
165+
it 'fails to match two urls' do
166+
destination = 'http://www.example.com/test?var=stuff'
167+
settings = 'http://www.example.com/othertest?var=stuff'
168+
assert !OneLogin::RubySaml::Utils.uri_match?(destination, settings)
169+
end
170+
171+
it "matches two URLs if the scheme case doesn't match" do
172+
destination = 'http://www.example.com/test?var=stuff'
173+
settings = 'HTTP://www.example.com/test?var=stuff'
174+
assert OneLogin::RubySaml::Utils.uri_match?(destination, settings)
175+
end
176+
177+
it "matches two URLs if the host case doesn't match" do
178+
destination = 'http://www.EXAMPLE.com/test?var=stuff'
179+
settings = 'http://www.example.com/test?var=stuff'
180+
assert OneLogin::RubySaml::Utils.uri_match?(destination, settings)
181+
end
182+
183+
it "fails to match two URLs if the path case doesn't match" do
184+
destination = 'http://www.example.com/TEST?var=stuff'
185+
settings = 'http://www.example.com/test?var=stuff'
186+
assert !OneLogin::RubySaml::Utils.uri_match?(destination, settings)
187+
end
188+
189+
it "fails to match two URLs if the query case doesn't match" do
190+
destination = 'http://www.example.com/test?var=stuff'
191+
settings = 'http://www.example.com/test?var=STUFF'
192+
assert !OneLogin::RubySaml::Utils.uri_match?(destination, settings)
193+
end
194+
195+
it 'matches two non urls' do
196+
destination = 'stuff'
197+
settings = 'stuff'
198+
assert OneLogin::RubySaml::Utils.uri_match?(destination, settings)
199+
end
200+
201+
it "fails to match two non urls" do
202+
destination = 'stuff'
203+
settings = 'not stuff'
204+
assert !OneLogin::RubySaml::Utils.uri_match?(destination, settings)
205+
end
206+
end
157207
end
158208
end

0 commit comments

Comments
 (0)