Skip to content

Commit 4fa2ed9

Browse files
committed
Merge pull request #222 from stevecrozz/detect_metadata_parse_failure
No more silent failure fetching idp metadata. (OneLogin::RubySaml::HttpError raised)
2 parents d42948c + cb11213 commit 4fa2ed9

4 files changed

Lines changed: 63 additions & 8 deletions

File tree

lib/onelogin/ruby-saml.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require 'onelogin/ruby-saml/response'
1010
require 'onelogin/ruby-saml/settings'
1111
require 'onelogin/ruby-saml/attribute_service'
12+
require 'onelogin/ruby-saml/http_error'
1213
require 'onelogin/ruby-saml/validation_error'
1314
require 'onelogin/ruby-saml/metadata'
1415
require 'onelogin/ruby-saml/idp_metadata_parser'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module OneLogin
2+
module RubySaml
3+
class HttpError < StandardError
4+
end
5+
end
6+
end
7+

lib/onelogin/ruby-saml/idp_metadata_parser.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ class IdpMetadataParser
2020
DSIG = "http://www.w3.org/2000/09/xmldsig#"
2121

2222
attr_reader :document
23+
attr_reader :response
2324

24-
# Parse the Identity Provider metadata and update the settings with the IdP values
25-
# @param url [String] Url where the XML of the Identity Provider Metadata is published.
26-
# @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
25+
# Parse the Identity Provider metadata and update the settings with the
26+
# IdP values
2727
#
28+
# @param (see IdpMetadataParser#get_idp_metadata)
29+
# @return (see IdpMetadataParser#get_idp_metadata)
30+
# @raise (see IdpMetadataParser#get_idp_metadata)
2831
def parse_remote(url, validate_cert = true)
2932
idp_metadata = get_idp_metadata(url, validate_cert)
3033
parse(idp_metadata)
@@ -51,7 +54,7 @@ def parse(idp_metadata)
5154
# @param url [String] Url where the XML of the Identity Provider Metadata is published.
5255
# @param validate_cert [Boolean] If true and the URL is HTTPs, the cert of the domain is checked.
5356
# @return [REXML::document] Parsed XML IdP metadata
54-
#
57+
# @raise [HttpError] Failure to fetch remote IdP metadata
5558
def get_idp_metadata(url, validate_cert)
5659
uri = URI.parse(url)
5760
if uri.scheme == "http"
@@ -75,7 +78,14 @@ def get_idp_metadata(url, validate_cert)
7578
get = Net::HTTP::Get.new(uri.request_uri)
7679
response = http.request(get)
7780
meta_text = response.body
81+
else
82+
raise ArgumentError.new("url must begin with http or https")
7883
end
84+
85+
unless response.is_a? Net::HTTPSuccess
86+
raise OneLogin::RubySaml::HttpError.new("Failed to fetch idp metadata")
87+
end
88+
7989
meta_text
8090
end
8191

test/idp_metadata_parser_test.rb

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@
33
require 'onelogin/ruby-saml/idp_metadata_parser'
44

55
class IdpMetadataParserTest < Minitest::Test
6+
class MockSuccessResponse < Net::HTTPSuccess
7+
# override parent's initialize
8+
def initialize; end
9+
10+
attr_accessor :body
11+
end
12+
13+
class MockFailureResponse < Net::HTTPNotFound
14+
# override parent's initialize
15+
def initialize; end
616

7-
class MockResponse
817
attr_accessor :body
918
end
1019

@@ -24,7 +33,7 @@ class MockResponse
2433

2534
describe "download and parse IdP descriptor file" do
2635
before do
27-
mock_response = MockResponse.new
36+
mock_response = MockSuccessResponse.new
2837
mock_response.body = idp_metadata
2938
@url = "https://example.com"
3039
uri = URI(@url)
@@ -34,7 +43,6 @@ class MockResponse
3443
@http.expects(:request).returns(mock_response)
3544
end
3645

37-
3846
it "extract settings from remote xml" do
3947
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
4048
settings = idp_metadata_parser.parse_remote(@url)
@@ -49,10 +57,39 @@ class MockResponse
4957

5058
it "accept self signed certificate if insturcted" do
5159
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
52-
settings = idp_metadata_parser.parse_remote(@url, false)
60+
idp_metadata_parser.parse_remote(@url, false)
5361

5462
assert_equal OpenSSL::SSL::VERIFY_NONE, @http.verify_mode
5563
end
5664
end
5765

66+
describe "download failure cases" do
67+
it "raises an exception when the url has no scheme" do
68+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
69+
70+
exception = assert_raises(ArgumentError) do
71+
idp_metadata_parser.parse_remote("blahblah")
72+
end
73+
74+
assert_equal("url must begin with http or https", exception.message)
75+
end
76+
77+
it "raises an exception when unable to download metadata" do
78+
mock_response = MockFailureResponse.new
79+
@url = "https://example.com"
80+
uri = URI(@url)
81+
82+
@http = Net::HTTP.new(uri.host, uri.port)
83+
Net::HTTP.expects(:new).returns(@http)
84+
@http.expects(:request).returns(mock_response)
85+
86+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
87+
88+
exception = assert_raises(OneLogin::RubySaml::HttpError) do
89+
idp_metadata_parser.parse_remote("https://example.hello.com/access/saml/idp.xml")
90+
end
91+
92+
assert_equal("Failed to fetch idp metadata", exception.message)
93+
end
94+
end
5895
end

0 commit comments

Comments
 (0)