Skip to content

Commit d23562b

Browse files
committed
Merge branch 'feat-http-timeout'
2 parents a924435 + d8c1667 commit d23562b

File tree

9 files changed

+55
-17
lines changed

9 files changed

+55
-17
lines changed

docs/howto/config.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ Example::
113113

114114
Whether debug information should be sent to the log file.
115115

116+
http_client_timeout
117+
^^^^^^^^^^^^^^^^^^^
118+
119+
Example::
120+
121+
http_client_timeout: 10
122+
123+
The timeout of HTTP requests, in seconds. Defaults to None.
124+
116125
additional_cert_files
117126
^^^^^^^^^^^^^^^^^^^^^
118127

src/saml2/config.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"name_id_format",
7777
"signing_algorithm",
7878
"digest_algorithm",
79+
"http_client_timeout",
7980
]
8081

8182
SP_ARGS = [
@@ -228,6 +229,7 @@ def __init__(self, homedir="."):
228229
self.delete_tmpfiles = True
229230
self.signing_algorithm = None
230231
self.digest_algorithm = None
232+
self.http_client_timeout = None
231233

232234
def setattr(self, context, attr, val):
233235
if context == "":
@@ -381,11 +383,13 @@ def load_metadata(self, metadata_conf):
381383
disable_validation = False
382384

383385
mds = MetadataStore(
384-
acs, self, ca_certs, disable_ssl_certificate_validation=disable_validation
386+
acs,
387+
self,
388+
ca_certs,
389+
disable_ssl_certificate_validation=disable_validation,
390+
http_client_timeout=self.http_client_timeout,
385391
)
386-
387392
mds.imp(metadata_conf)
388-
389393
return mds
390394

391395
def endpoint(self, service, binding=None, context=None):

src/saml2/ecp_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ def __init__(self, user, passwd, sp="", idp=None, metadata_file=None,
7878
self._verbose = verbose
7979

8080
if metadata_file:
81-
self._metadata = MetadataStore([saml, samlp], None, config)
81+
self._metadata = MetadataStore([saml, samlp], None, config,
82+
http_client_timeout=config.http_client_timeout)
8283
self._metadata.load("local", metadata_file)
8384
logger.debug("Loaded metadata from '%s'", metadata_file)
8485
else:

src/saml2/entity.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def __init__(self, entity_type, config=None, config_file="",
165165
continue
166166

167167
if _val.startswith("http"):
168-
r = requests.request("GET", _val)
168+
r = requests.request("GET", _val, timeout=self.config.http_client_timeout)
169169
if r.status_code == 200:
170170
tmp = make_temp(r.text, ".pem", False, self.config.delete_tmpfiles)
171171
setattr(self.config, item, tmp.name)
@@ -175,7 +175,7 @@ def __init__(self, entity_type, config=None, config_file="",
175175

176176
HTTPBase.__init__(self, self.config.verify_ssl_cert,
177177
self.config.ca_certs, self.config.key_file,
178-
self.config.cert_file)
178+
self.config.cert_file, self.config.http_client_timeout)
179179

180180
if self.config.vorg:
181181
for vo in self.config.vorg.values():

src/saml2/httpbase.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def dict2set_list(dic):
100100

101101
class HTTPBase(object):
102102
def __init__(self, verify=True, ca_bundle=None, key_file=None,
103-
cert_file=None):
103+
cert_file=None, http_client_timeout=None):
104104
self.request_args = {"allow_redirects": False}
105105
#self.cookies = {}
106106
self.cookiejar = http_cookiejar.CookieJar()
@@ -111,6 +111,7 @@ def __init__(self, verify=True, ca_bundle=None, key_file=None,
111111
self.request_args["verify"] = ca_bundle
112112
if key_file:
113113
self.request_args["cert"] = (cert_file, key_file)
114+
self.request_args["timeout"] = http_client_timeout
114115

115116
self.sec = None
116117
self.user = None

src/saml2/mdstore.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,8 @@ def sha1_entity_transform(entity_id):
928928
return transform
929929

930930
def __init__(self, url=None, security=None, cert=None,
931-
entity_transform=None, freshness_period=None, **kwargs):
931+
entity_transform=None, freshness_period=None,
932+
http_client_timeout=None, **kwargs):
932933
"""
933934
:params url: mdx service url
934935
:params security: SecurityContext()
@@ -940,6 +941,7 @@ def __init__(self, url=None, security=None, cert=None,
940941
sha1 transformation.
941942
:params freshness_period: a duration in the format described at
942943
https://www.w3.org/TR/xmlschema-2/#duration
944+
:params http_client_timeout: timeout of http requests
943945
"""
944946
super(MetaDataMDX, self).__init__(None, **kwargs)
945947
if not url:
@@ -956,6 +958,7 @@ def __init__(self, url=None, security=None, cert=None,
956958
self.security = security
957959
self.freshness_period = freshness_period or DEFAULT_FRESHNESS_PERIOD
958960
self.expiration_date = {}
961+
self.http_client_timeout = http_client_timeout
959962

960963
# We assume that the MDQ server will return a single entity
961964
# described by a single <EntityDescriptor> element. The protocol
@@ -976,7 +979,8 @@ def _fetch_metadata(self, item):
976979
url=self.url, id=self.entity_transform(item)
977980
)
978981

979-
response = requests.get(mdx_url, headers={"Accept": SAML_METADATA_CONTENT_TYPE})
982+
response = requests.get(mdx_url, headers={"Accept": SAML_METADATA_CONTENT_TYPE},
983+
timeout=self.http_client_timeout)
980984
if response.status_code != 200:
981985
error_msg = "Fething {item}: Got response status {status}".format(
982986
item=item, status=response.status_code
@@ -1022,7 +1026,7 @@ class MetadataStore(MetaData):
10221026
def __init__(self, attrc, config, ca_certs=None,
10231027
check_validity=True,
10241028
disable_ssl_certificate_validation=False,
1025-
filter=None):
1029+
filter=None, http_client_timeout=None):
10261030
"""
10271031
:params attrc:
10281032
:params config: Config()
@@ -1032,16 +1036,17 @@ def __init__(self, attrc, config, ca_certs=None,
10321036
MetaData.__init__(self, attrc, check_validity=check_validity)
10331037

10341038
if disable_ssl_certificate_validation:
1035-
self.http = HTTPBase(verify=False, ca_bundle=ca_certs)
1039+
self.http = HTTPBase(verify=False, ca_bundle=ca_certs, http_client_timeout=http_client_timeout)
10361040
else:
1037-
self.http = HTTPBase(verify=True, ca_bundle=ca_certs)
1041+
self.http = HTTPBase(verify=True, ca_bundle=ca_certs, http_client_timeout=http_client_timeout)
10381042

10391043
self.security = security_context(config)
10401044
self.ii = 0
10411045
self.metadata = {}
10421046
self.check_validity = check_validity
10431047
self.filter = filter
10441048
self.to_old = {}
1049+
self.http_client_timeout = http_client_timeout
10451050

10461051
def load(self, *args, **kwargs):
10471052
if self.filter:
@@ -1100,11 +1105,12 @@ def load(self, *args, **kwargs):
11001105
security = self.security
11011106
entity_transform = kwargs.get('entity_transform', None)
11021107
_md = MetaDataMDX(url, security, cert, entity_transform,
1103-
freshness_period=freshness_period)
1108+
freshness_period=freshness_period,
1109+
http_client_timeout=self.http_client_timeout)
11041110
else:
11051111
key = args[1]
11061112
url = args[1]
1107-
_md = MetaDataMDX(url)
1113+
_md = MetaDataMDX(url, http_client_timeout=self.http_client_timeout)
11081114
else:
11091115
raise SAMLError("Unknown metadata type '%s'" % typ)
11101116
_md.load()

tests/sp_1_conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@
4848
},
4949
],
5050
"secret": "0123456789",
51+
"http_client_timeout": 10,
5152
}

tests/test_30_mdstore.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,16 @@ def test_mdx_service():
345345
assert len(certs) == 1
346346

347347

348+
@patch('saml2.httpbase.requests.get')
349+
def test_mdx_service_request_timeout(mock_request):
350+
entity_id = "http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php"
351+
url = "http://mdx.example.com/entities/{}".format(MetaDataMDX.sha1_entity_transform(entity_id))
352+
353+
mdx = MetaDataMDX("http://mdx.example.com", http_client_timeout=10)
354+
mdx.service(entity_id, "idpsso_descriptor", "single_sign_on_service")
355+
mock_request.assert_called_with(url, headers={'Accept': 'application/samlmetadata+xml'}, timeout=10)
356+
357+
348358
@responses.activate
349359
def test_mdx_single_sign_on_service():
350360
entity_id = "http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php"
@@ -462,12 +472,14 @@ def test_load_extern_incommon(mock_request):
462472

463473
sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"])
464474
mds = MetadataStore(ATTRCONV, sec_config,
465-
disable_ssl_certificate_validation=True)
475+
disable_ssl_certificate_validation=True,
476+
http_client_timeout=10)
466477

467478
mds.imp(METADATACONF["10"])
468479
print(mds)
469480
assert mds
470481
assert len(mds.keys())
482+
mock_request.assert_called_with('GET', 'http://md.incommon.org/InCommon/InCommon-metadata-export.xml', allow_redirects=False, verify=False, timeout=10)
471483

472484

473485
def test_load_local():
@@ -495,10 +507,13 @@ def test_load_remote_encoding(mock_request):
495507

496508
crypto = sigver._get_xmlsec_cryptobackend()
497509
sc = sigver.SecurityContext(crypto, key_type="", cert_type="")
498-
httpc = HTTPBase()
499-
mds = MetaDataExtern(ATTRCONV, 'http://metadata.aai.switch.ch/metadata.aaitest.xml', sc, full_path('SWITCHaaiRootCA.crt.pem'), httpc)
510+
url = 'http://metadata.aai.switch.ch/metadata.aaitest.xml'
511+
httpc = HTTPBase(http_client_timeout=10)
512+
mds = MetaDataExtern(ATTRCONV, url, sc, full_path('SWITCHaaiRootCA.crt.pem'), httpc)
500513
mds.load()
501514

515+
mock_request.assert_called_with('GET', url, allow_redirects=False, verify=True, timeout=10)
516+
502517

503518
def test_load_string():
504519
sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"])

tests/test_31_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ def test_3():
338338
assert cnf.secret == "0123456789"
339339
assert cnf.metadata is not None
340340
assert cnf.attribute_converters is not None
341+
assert cnf.http_client_timeout == 10
341342

342343

343344
def test_sp():

0 commit comments

Comments
 (0)