Skip to content

Commit

Permalink
✨ [SAML-Toolkits#403] Fetch IDP metadata using requests
Browse files Browse the repository at this point in the history
Using requests allows us to easily customize the CA_BUNDLE to use
when verifying the server certificate, instead of having to disable SSL
certificate verification alltogether.
  • Loading branch information
sergei-maertens committed Jul 10, 2024
1 parent fd02e44 commit 78033d9
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 25 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies = [
"lxml>=4.6.5,!=4.7.0",
"xmlsec>=1.3.9",
"isodate>=0.6.1",
"requests",
]
requires-python = ">=3.7"

Expand Down Expand Up @@ -119,4 +120,4 @@ ignore_errors = true


[tool.coverage.html]
directory = "coverage_html_report"
directory = "coverage_html_report"
25 changes: 11 additions & 14 deletions src/onelogin/saml2/idp_metadata_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

from copy import deepcopy

try:
import urllib.request as urllib2
except ImportError:
import urllib2
import requests

import ssl

Expand All @@ -30,6 +27,13 @@ class OneLogin_Saml2_IdPMetadataParser(object):
def get_metadata(cls, url, validate_cert=True, timeout=None, headers=None):
"""
Gets the metadata XML from the provided URL
The metadata is retrieved with the ``requests`` library. If you need to support
server certificates that are not readily available in the trust store, use the
``REQUESTS_CA_BUNDLE`` environment variable. See the
`advanced documentation <https://requests.readthedocs.io/en/latest/user/advanced/>`_
for more information.
:param url: Url where the XML of the Identity Provider Metadata is published.
:type url: string
Expand All @@ -46,16 +50,9 @@ def get_metadata(cls, url, validate_cert=True, timeout=None, headers=None):
"""
valid = False

request = urllib2.Request(url, headers=headers or {})

if validate_cert:
response = urllib2.urlopen(request, timeout=timeout)
else:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
response = urllib2.urlopen(request, context=ctx, timeout=timeout)
xml = response.read()
response = requests.get(url, headers=headers, verify=validate_cert, timeout=timeout)
response.raise_for_status()
xml = response.content

if xml:
try:
Expand Down
15 changes: 5 additions & 10 deletions tests/src/OneLogin/saml2_tests/idp_metadata_parser_test.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
# -*- coding: utf-8 -*-


try:
from urllib.error import URLError
except ImportError:
from urllib2 import URLError

from copy import deepcopy
import json
from os.path import dirname, join, exists
from lxml.etree import XMLSyntaxError
from requests import RequestException
import unittest

from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
Expand Down Expand Up @@ -51,7 +46,7 @@ def testGetMetadata(self):
try:
data = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False)
self.assertTrue(data is not None and data is not {})
except URLError:
except RequestException:
pass

def testGetMetadataWithHeaders(self):
Expand All @@ -70,7 +65,7 @@ def testParseRemote(self):

try:
data = OneLogin_Saml2_IdPMetadataParser.parse_remote("https://raw.githubusercontent.com/SAML-Toolkits/python3-saml/master/tests/data/metadata/testshib-providers.xml", validate_cert=False)
except URLError:
except RequestException:
xml = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml"))
data = OneLogin_Saml2_IdPMetadataParser.parse(xml)

Expand Down Expand Up @@ -162,7 +157,7 @@ def test_parse_testshib_required_binding_sso_redirect(self):
"""
try:
xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://idp.testshib.org/idp/shibboleth")
except URLError:
except RequestException:
xmldoc = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml"))

# Parse, require SSO REDIRECT binding, implicitly.
Expand Down Expand Up @@ -196,7 +191,7 @@ def test_parse_testshib_required_binding_sso_post(self):
"""
try:
xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata("https://idp.testshib.org/idp/shibboleth")
except URLError:
except RequestException:
xmldoc = self.file_contents(join(self.data_path, "metadata", "testshib-providers.xml"))

# Parse, require POST binding.
Expand Down

0 comments on commit 78033d9

Please sign in to comment.