-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add kerberos support to authentication pip (when supported) #4854
Changes from all commits
08dc580
c936e14
87086bd
e6ce814
f9391d3
6db53d1
a8e46ef
5965c29
dc3936b
37fb3cf
84f62ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add kerberos support to possible authenticators, when available. Vendor in requests_kerberos 0.11.0. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Vendored requests_kerberos at requests_kerberos==0.11.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,14 @@ | |
) | ||
|
||
|
||
try: | ||
from pip._vendor.requests_kerberos import HTTPKerberosAuth | ||
from pip._vendor.requests_kerberos import kerberos_ as ik | ||
_KERBEROS_AVAILABLE = True | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some reason other that If not, please import these unconditionally at the top of the file, since pip does vendoring to ensure that vendored packages are always available. Basically, in not-broken installations of pip, this variable will always be True, which makes it redundant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "If py/winkerberos is not installed, the behaviour is the same as before": requests_kerberos on its own will not make kerberos work, one has to have either py/winkerberos installed (which we cannot vendor because they are compiled/platform dependent?). requests_kerberos in that case will fail with an ImportError. |
||
|
||
except ImportError: | ||
_KERBEROS_AVAILABLE = False | ||
|
||
__all__ = ['get_file_content', | ||
'is_url', 'url_to_path', 'path_to_url', | ||
'is_archive_file', 'unpack_vcs_link', | ||
|
@@ -494,6 +502,47 @@ def save_credentials(self, resp, **kwargs): | |
logger.exception('Failed to save credentials') | ||
|
||
|
||
class MultiAuth(AuthBase): | ||
def __init__(self, initial_auth=None, *auths): | ||
if initial_auth is None: | ||
self.initial_auth = MultiDomainBasicAuth(prompting=False) | ||
else: | ||
self.initial_auth = initial_auth | ||
|
||
self.auths = auths | ||
|
||
def __call__(self, req): | ||
req = self.initial_auth(req) | ||
self._register_hook(req, 0) # register hook after auth itself | ||
return req | ||
|
||
def _register_hook(self, req, i): | ||
if i >= len(self.auths): | ||
return | ||
|
||
def hook(resp, **kwargs): | ||
self.handle_response(resp, i, **kwargs) | ||
|
||
req.register_hook("response", hook) | ||
|
||
def handle_response(self, resp, i, **kwargs): | ||
if resp.status_code != 401: # authorization required | ||
return resp | ||
|
||
# clear response | ||
resp.content | ||
resp.raw.release_conn() | ||
|
||
req = self.auths[i](resp.request) # deletegate to ith auth | ||
logger.info('registering hook {}'.format(i + 1)) | ||
self._register_hook(req, i + 1) # register hook after auth itself | ||
|
||
new_resp = resp.connection.send(req, **kwargs) | ||
new_resp.history.append(resp) | ||
|
||
return new_resp | ||
|
||
|
||
class LocalFSAdapter(BaseAdapter): | ||
|
||
def send(self, request, stream=None, timeout=None, verify=None, cert=None, | ||
|
@@ -579,8 +628,10 @@ def __init__(self, *args, **kwargs): | |
""" | ||
retries = kwargs.pop("retries", 0) | ||
cache = kwargs.pop("cache", None) | ||
|
||
trusted_hosts = kwargs.pop("trusted_hosts", []) # type: List[str] | ||
index_urls = kwargs.pop("index_urls", None) | ||
prompting = kwargs.pop("prompting", True) | ||
|
||
super(PipSession, self).__init__(*args, **kwargs) | ||
|
||
|
@@ -592,7 +643,21 @@ def __init__(self, *args, **kwargs): | |
self.headers["User-Agent"] = user_agent() | ||
|
||
# Attach our Authentication handler to the session | ||
self.auth = MultiDomainBasicAuth(index_urls=index_urls) | ||
no_prompt = MultiDomainBasicAuth(prompting=False) | ||
prompt = MultiDomainBasicAuth(prompting=True) | ||
prompt.passwords = no_prompt.passwords # share same dict of passwords | ||
|
||
if _KERBEROS_AVAILABLE and prompting: | ||
auths = [no_prompt, HTTPKerberosAuth(ik.REQUIRED), prompt] | ||
elif _KERBEROS_AVAILABLE and not prompting: | ||
auths = [no_prompt, HTTPKerberosAuth(ik.REQUIRED)] | ||
else: | ||
auths = [MultiDomainBasicAuth( | ||
prompting=prompting, | ||
index_urls=index_urls | ||
)] | ||
|
||
self.auth = MultiAuth(*auths) | ||
|
||
# Create our urllib3.Retry instance which will allow us to customize | ||
# how we handle retries. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
""" | ||
requests Kerberos/GSSAPI authentication library | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Requests is an HTTP library, written in Python, for human beings. This library | ||
adds optional Kerberos/GSSAPI authentication support and supports mutual | ||
authentication. Basic GET usage: | ||
|
||
>>> import pip._vendor.requests | ||
>>> from pip._vendor.requests_kerberos import HTTPKerberosAuth | ||
>>> r = pip._vendor.requests.get("http://example.org", auth=HTTPKerberosAuth()) | ||
|
||
The entire `requests.api` should be supported. | ||
""" | ||
import logging | ||
|
||
from .kerberos_ import HTTPKerberosAuth, REQUIRED, OPTIONAL, DISABLED | ||
from .exceptions import MutualAuthenticationError | ||
from .compat import NullHandler | ||
|
||
logging.getLogger(__name__).addHandler(NullHandler()) | ||
|
||
__all__ = ('HTTPKerberosAuth', 'MutualAuthenticationError', 'REQUIRED', | ||
'OPTIONAL', 'DISABLED') | ||
__version__ = '0.11.0' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
""" | ||
Compatibility library for older versions of python | ||
""" | ||
import sys | ||
|
||
# python 2.7 introduced a NullHandler which we want to use, but to support | ||
# older versions, we implement our own if needed. | ||
if sys.version_info[:2] > (2, 6): | ||
from logging import NullHandler | ||
else: | ||
from logging import Handler | ||
class NullHandler(Handler): | ||
def emit(self, record): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
""" | ||
requests_kerberos.exceptions | ||
~~~~~~~~~~~~~~~~~~~ | ||
|
||
This module contains the set of exceptions. | ||
|
||
""" | ||
from pip._vendor.requests.exceptions import RequestException | ||
|
||
|
||
class MutualAuthenticationError(RequestException): | ||
"""Mutual Authentication Error""" | ||
|
||
class KerberosExchangeError(RequestException): | ||
"""Kerberos Exchange Failed Error""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version number should be changed to the release it will actually be in.