summaryrefslogtreecommitdiff
path: root/Lib/urllib/request.py
diff options
context:
space:
mode:
authorR David Murray <rdmurray@bitdance.com>2015-04-16 16:36:18 -0400
committerR David Murray <rdmurray@bitdance.com>2015-04-16 16:36:18 -0400
commit4c7f995e805f4fddcf54b90f35ea30c7e26a4a95 (patch)
tree870ddf9b8e0d8f65dddb5f990c58d69968979d66 /Lib/urllib/request.py
parent6297fecbd1fc534176d65a6a27c01b4667a6b417 (diff)
downloadcpython-git-4c7f995e805f4fddcf54b90f35ea30c7e26a4a95.tar.gz
#7159: generalize urllib prior auth support.
This fix is a superset of the functionality introduced by the issue #19494 enhancement, and supersedes that fix. Instead of a new handler, we have a new password manager that tracks whether we should send the auth for a given uri. This allows us to say "always send", satisfying #19494, or track that we've succeeded in auth and send the creds right away on every *subsequent* request. The support for using the password manager is added to AbstractBasicAuth, which means the proxy handler also now can handle prior auth if passed the new password manager. Patch by Akshit Khurana, docs mostly by me.
Diffstat (limited to 'Lib/urllib/request.py')
-rw-r--r--Lib/urllib/request.py78
1 files changed, 60 insertions, 18 deletions
diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py
index 2e436ecfda..eada0a9132 100644
--- a/Lib/urllib/request.py
+++ b/Lib/urllib/request.py
@@ -120,9 +120,10 @@ __all__ = [
'Request', 'OpenerDirector', 'BaseHandler', 'HTTPDefaultErrorHandler',
'HTTPRedirectHandler', 'HTTPCookieProcessor', 'ProxyHandler',
'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm',
- 'AbstractBasicAuthHandler', 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler',
- 'AbstractDigestAuthHandler', 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler',
- 'HTTPHandler', 'FileHandler', 'FTPHandler', 'CacheFTPHandler', 'DataHandler',
+ 'HTTPPasswordMgrWithPriorAuth', 'AbstractBasicAuthHandler',
+ 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', 'AbstractDigestAuthHandler',
+ 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', 'HTTPHandler',
+ 'FileHandler', 'FTPHandler', 'CacheFTPHandler', 'DataHandler',
'UnknownHandler', 'HTTPErrorProcessor',
# Functions
'urlopen', 'install_opener', 'build_opener',
@@ -835,6 +836,37 @@ class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
return HTTPPasswordMgr.find_user_password(self, None, authuri)
+class HTTPPasswordMgrWithPriorAuth(HTTPPasswordMgrWithDefaultRealm):
+
+ def __init__(self, *args, **kwargs):
+ self.authenticated = {}
+ super().__init__(*args, **kwargs)
+
+ def add_password(self, realm, uri, user, passwd, is_authenticated=False):
+ self.update_authenticated(uri, is_authenticated)
+ # Add a default for prior auth requests
+ if realm is not None:
+ super().add_password(None, uri, user, passwd)
+ super().add_password(realm, uri, user, passwd)
+
+ def update_authenticated(self, uri, is_authenticated=False):
+ # uri could be a single URI or a sequence
+ if isinstance(uri, str):
+ uri = [uri]
+
+ for default_port in True, False:
+ for u in uri:
+ reduced_uri = self.reduce_uri(u, default_port)
+ self.authenticated[reduced_uri] = is_authenticated
+
+ def is_authenticated(self, authuri):
+ for default_port in True, False:
+ reduced_authuri = self.reduce_uri(authuri, default_port)
+ for uri in self.authenticated:
+ if self.is_suburi(uri, reduced_authuri):
+ return self.authenticated[uri]
+
+
class AbstractBasicAuthHandler:
# XXX this allows for multiple auth-schemes, but will stupidly pick
@@ -889,6 +921,31 @@ class AbstractBasicAuthHandler:
else:
return None
+ def http_request(self, req):
+ if (not hasattr(self.passwd, 'is_authenticated') or
+ not self.passwd.is_authenticated(req.full_url)):
+ return req
+
+ if not req.has_header('Authorization'):
+ user, passwd = self.passwd.find_user_password(None, req.full_url)
+ credentials = '{0}:{1}'.format(user, passwd).encode()
+ auth_str = base64.standard_b64encode(credentials).decode()
+ req.add_unredirected_header('Authorization',
+ 'Basic {}'.format(auth_str.strip()))
+ return req
+
+ def http_response(self, req, response):
+ if hasattr(self.passwd, 'is_authenticated'):
+ if 200 <= response.code < 300:
+ self.passwd.update_authenticated(req.full_url, True)
+ else:
+ self.passwd.update_authenticated(req.full_url, False)
+ return response
+
+ https_request = http_request
+ https_response = http_response
+
+
class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
@@ -916,21 +973,6 @@ class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
return response
-class HTTPBasicPriorAuthHandler(HTTPBasicAuthHandler):
- handler_order = 400
-
- def http_request(self, req):
- if not req.has_header('Authorization'):
- user, passwd = self.passwd.find_user_password(None, req.host)
- credentials = '{0}:{1}'.format(user, passwd).encode()
- auth_str = base64.standard_b64encode(credentials).decode()
- req.add_unredirected_header('Authorization',
- 'Basic {}'.format(auth_str.strip()))
- return req
-
- https_request = http_request
-
-
# Return n random bytes.
_randombytes = os.urandom