Urls client cert auth (#18141)

* Build HTTPSClientAuthHandler more similarly to how HTTPSHandler works

* Add docs for new client cert authentication

* Support older versions of python

* Simplify logic

* Initial support for client certs in urls.py

* Add an extra test

* Add a get_url test for client cert auth

* Add additional test for client cert auth, with validation and ssl mismatch

* Skip assert when http tester not available

* Update version_added for new options
This commit is contained in:
Matt Martz 2017-04-07 11:54:37 -05:00 committed by Toshio Kuratomi
commit 621e27b5dd
6 changed files with 138 additions and 4 deletions

View file

@ -411,6 +411,33 @@ if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib_request, 'HTTPSHandler
https_request = AbstractHTTPHandler.do_request_
class HTTPSClientAuthHandler(urllib_request.HTTPSHandler):
'''Handles client authentication via cert/key
This is a fairly lightweight extension on HTTPSHandler, and can be used
in place of HTTPSHandler
'''
def __init__(self, client_cert=None, client_key=None, **kwargs):
urllib_request.HTTPSHandler.__init__(self, **kwargs)
self.client_cert = client_cert
self.client_key = client_key
def https_open(self, req):
return self.do_open(self._build_https_connection, req)
def _build_https_connection(self, host, **kwargs):
kwargs.update({
'cert_file': self.client_cert,
'key_file': self.client_key,
})
try:
kwargs['context'] = self._context
except AttributeError:
pass
return httplib.HTTPSConnection(host, **kwargs)
def generic_urlparse(parts):
'''
Returns a dictionary of url parts as parsed by urlparse,
@ -796,7 +823,8 @@ def maybe_add_ssl_handler(url, validate_certs):
def open_url(url, data=None, headers=None, method=None, use_proxy=True,
force=False, last_mod_time=None, timeout=10, validate_certs=True,
url_username=None, url_password=None, http_agent=None,
force_basic_auth=False, follow_redirects='urllib2'):
force_basic_auth=False, follow_redirects='urllib2',
client_cert=None, client_key=None):
'''
Sends a request via HTTP(S) or FTP using urllib2 (Python2) or urllib (Python3)
@ -875,7 +903,12 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
context.options |= ssl.OP_NO_SSLv3
context.verify_mode = ssl.CERT_NONE
context.check_hostname = False
handlers.append(urllib_request.HTTPSHandler(context=context))
handlers.append(HTTPSClientAuthHandler(client_cert=client_cert,
client_key=client_key,
context=context))
elif client_cert:
handlers.append(HTTPSClientAuthHandler(client_cert=client_cert,
client_key=client_key))
# pre-2.6 versions of python cannot use the custom https
# handler, since the socket class is lacking create_connection.
@ -952,7 +985,8 @@ def url_argument_spec():
url_username=dict(required=False),
url_password=dict(required=False, no_log=True),
force_basic_auth=dict(required=False, type='bool', default='no'),
client_cert=dict(required=False, type='path', default=None),
client_key=dict(required=False, type='path', default=None),
)
@ -1001,6 +1035,9 @@ def fetch_url(module, url, data=None, headers=None, method=None,
follow_redirects = module.params.get('follow_redirects', 'urllib2')
client_cert = module.params.get('client_cert')
client_key = module.params.get('client_key')
r = None
info = dict(url=url)
try:
@ -1008,7 +1045,8 @@ def fetch_url(module, url, data=None, headers=None, method=None,
use_proxy=use_proxy, force=force, last_mod_time=last_mod_time, timeout=timeout,
validate_certs=validate_certs, url_username=username,
url_password=password, http_agent=http_agent, force_basic_auth=force_basic_auth,
follow_redirects=follow_redirects)
follow_redirects=follow_redirects, client_cert=client_cert,
client_key=client_key)
info.update(r.info())
info.update(dict(msg="OK (%s bytes)" % r.headers.get('Content-Length', 'unknown'), url=r.geturl(), status=r.code))
except NoSSLError: