diff options
author | Shubham Saini <shubham6405@gmail.com> | 2018-12-11 10:01:23 +0000 |
---|---|---|
committer | Shubham Saini <shubham6405@gmail.com> | 2018-12-11 10:01:23 +0000 |
commit | 68df54d6629ec019142eb149dd037774f2d11e7c (patch) | |
tree | 345bc22d46b4e01a4ba8303b94278952a4ed2b9e /venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py |
First commit
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py new file mode 100644 index 0000000..73e4534 --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/requests/auth.py | |||
@@ -0,0 +1,293 @@ | |||
1 | # -*- coding: utf-8 -*- | ||
2 | |||
3 | """ | ||
4 | requests.auth | ||
5 | ~~~~~~~~~~~~~ | ||
6 | |||
7 | This module contains the authentication handlers for Requests. | ||
8 | """ | ||
9 | |||
10 | import os | ||
11 | import re | ||
12 | import time | ||
13 | import hashlib | ||
14 | import threading | ||
15 | import warnings | ||
16 | |||
17 | from base64 import b64encode | ||
18 | |||
19 | from .compat import urlparse, str, basestring | ||
20 | from .cookies import extract_cookies_to_jar | ||
21 | from ._internal_utils import to_native_string | ||
22 | from .utils import parse_dict_header | ||
23 | |||
24 | CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' | ||
25 | CONTENT_TYPE_MULTI_PART = 'multipart/form-data' | ||
26 | |||
27 | |||
28 | def _basic_auth_str(username, password): | ||
29 | """Returns a Basic Auth string.""" | ||
30 | |||
31 | # "I want us to put a big-ol' comment on top of it that | ||
32 | # says that this behaviour is dumb but we need to preserve | ||
33 | # it because people are relying on it." | ||
34 | # - Lukasa | ||
35 | # | ||
36 | # These are here solely to maintain backwards compatibility | ||
37 | # for things like ints. This will be removed in 3.0.0. | ||
38 | if not isinstance(username, basestring): | ||
39 | warnings.warn( | ||
40 | "Non-string usernames will no longer be supported in Requests " | ||
41 | "3.0.0. Please convert the object you've passed in ({0!r}) to " | ||
42 | "a string or bytes object in the near future to avoid " | ||
43 | "problems.".format(username), | ||
44 | category=DeprecationWarning, | ||
45 | ) | ||
46 | username = str(username) | ||
47 | |||
48 | if not isinstance(password, basestring): | ||
49 | warnings.warn( | ||
50 | "Non-string passwords will no longer be supported in Requests " | ||
51 | "3.0.0. Please convert the object you've passed in ({0!r}) to " | ||
52 | "a string or bytes object in the near future to avoid " | ||
53 | "problems.".format(password), | ||
54 | category=DeprecationWarning, | ||
55 | ) | ||
56 | password = str(password) | ||
57 | # -- End Removal -- | ||
58 | |||
59 | if isinstance(username, str): | ||
60 | username = username.encode('latin1') | ||
61 | |||
62 | if isinstance(password, str): | ||
63 | password = password.encode('latin1') | ||
64 | |||
65 | authstr = 'Basic ' + to_native_string( | ||
66 | b64encode(b':'.join((username, password))).strip() | ||
67 | ) | ||
68 | |||
69 | return authstr | ||
70 | |||
71 | |||
72 | class AuthBase(object): | ||
73 | """Base class that all auth implementations derive from""" | ||
74 | |||
75 | def __call__(self, r): | ||
76 | raise NotImplementedError('Auth hooks must be callable.') | ||
77 | |||
78 | |||
79 | class HTTPBasicAuth(AuthBase): | ||
80 | """Attaches HTTP Basic Authentication to the given Request object.""" | ||
81 | |||
82 | def __init__(self, username, password): | ||
83 | self.username = username | ||
84 | self.password = password | ||
85 | |||
86 | def __eq__(self, other): | ||
87 | return all([ | ||
88 | self.username == getattr(other, 'username', None), | ||
89 | self.password == getattr(other, 'password', None) | ||
90 | ]) | ||
91 | |||
92 | def __ne__(self, other): | ||
93 | return not self == other | ||
94 | |||
95 | def __call__(self, r): | ||
96 | r.headers['Authorization'] = _basic_auth_str(self.username, self.password) | ||
97 | return r | ||
98 | |||
99 | |||
100 | class HTTPProxyAuth(HTTPBasicAuth): | ||
101 | """Attaches HTTP Proxy Authentication to a given Request object.""" | ||
102 | |||
103 | def __call__(self, r): | ||
104 | r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password) | ||
105 | return r | ||
106 | |||
107 | |||
108 | class HTTPDigestAuth(AuthBase): | ||
109 | """Attaches HTTP Digest Authentication to the given Request object.""" | ||
110 | |||
111 | def __init__(self, username, password): | ||
112 | self.username = username | ||
113 | self.password = password | ||
114 | # Keep state in per-thread local storage | ||
115 | self._thread_local = threading.local() | ||
116 | |||
117 | def init_per_thread_state(self): | ||
118 | # Ensure state is initialized just once per-thread | ||
119 | if not hasattr(self._thread_local, 'init'): | ||
120 | self._thread_local.init = True | ||
121 | self._thread_local.last_nonce = '' | ||
122 | self._thread_local.nonce_count = 0 | ||
123 | self._thread_local.chal = {} | ||
124 | self._thread_local.pos = None | ||
125 | self._thread_local.num_401_calls = None | ||
126 | |||
127 | def build_digest_header(self, method, url): | ||
128 | """ | ||
129 | :rtype: str | ||
130 | """ | ||
131 | |||
132 | realm = self._thread_local.chal['realm'] | ||
133 | nonce = self._thread_local.chal['nonce'] | ||
134 | qop = self._thread_local.chal.get('qop') | ||
135 | algorithm = self._thread_local.chal.get('algorithm') | ||
136 | opaque = self._thread_local.chal.get('opaque') | ||
137 | hash_utf8 = None | ||
138 | |||
139 | if algorithm is None: | ||
140 | _algorithm = 'MD5' | ||
141 | else: | ||
142 | _algorithm = algorithm.upper() | ||
143 | # lambdas assume digest modules are imported at the top level | ||
144 | if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': | ||
145 | def md5_utf8(x): | ||
146 | if isinstance(x, str): | ||
147 | x = x.encode('utf-8') | ||
148 | return hashlib.md5(x).hexdigest() | ||
149 | hash_utf8 = md5_utf8 | ||
150 | elif _algorithm == 'SHA': | ||
151 | def sha_utf8(x): | ||
152 | if isinstance(x, str): | ||
153 | x = x.encode('utf-8') | ||
154 | return hashlib.sha1(x).hexdigest() | ||
155 | hash_utf8 = sha_utf8 | ||
156 | |||
157 | KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) | ||
158 | |||
159 | if hash_utf8 is None: | ||
160 | return None | ||
161 | |||
162 | # XXX not implemented yet | ||
163 | entdig = None | ||
164 | p_parsed = urlparse(url) | ||
165 | #: path is request-uri defined in RFC 2616 which should not be empty | ||
166 | path = p_parsed.path or "/" | ||
167 | if p_parsed.query: | ||
168 | path += '?' + p_parsed.query | ||
169 | |||
170 | A1 = '%s:%s:%s' % (self.username, realm, self.password) | ||
171 | A2 = '%s:%s' % (method, path) | ||
172 | |||
173 | HA1 = hash_utf8(A1) | ||
174 | HA2 = hash_utf8(A2) | ||
175 | |||
176 | if nonce == self._thread_local.last_nonce: | ||
177 | self._thread_local.nonce_count += 1 | ||
178 | else: | ||
179 | self._thread_local.nonce_count = 1 | ||
180 | ncvalue = '%08x' % self._thread_local.nonce_count | ||
181 | s = str(self._thread_local.nonce_count).encode('utf-8') | ||
182 | s += nonce.encode('utf-8') | ||
183 | s += time.ctime().encode('utf-8') | ||
184 | s += os.urandom(8) | ||
185 | |||
186 | cnonce = (hashlib.sha1(s).hexdigest()[:16]) | ||
187 | if _algorithm == 'MD5-SESS': | ||
188 | HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) | ||
189 | |||
190 | if not qop: | ||
191 | respdig = KD(HA1, "%s:%s" % (nonce, HA2)) | ||
192 | elif qop == 'auth' or 'auth' in qop.split(','): | ||
193 | noncebit = "%s:%s:%s:%s:%s" % ( | ||
194 | nonce, ncvalue, cnonce, 'auth', HA2 | ||
195 | ) | ||
196 | respdig = KD(HA1, noncebit) | ||
197 | else: | ||
198 | # XXX handle auth-int. | ||
199 | return None | ||
200 | |||
201 | self._thread_local.last_nonce = nonce | ||
202 | |||
203 | # XXX should the partial digests be encoded too? | ||
204 | base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ | ||
205 | 'response="%s"' % (self.username, realm, nonce, path, respdig) | ||
206 | if opaque: | ||
207 | base += ', opaque="%s"' % opaque | ||
208 | if algorithm: | ||
209 | base += ', algorithm="%s"' % algorithm | ||
210 | if entdig: | ||
211 | base += ', digest="%s"' % entdig | ||
212 | if qop: | ||
213 | base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) | ||
214 | |||
215 | return 'Digest %s' % (base) | ||
216 | |||
217 | def handle_redirect(self, r, **kwargs): | ||
218 | """Reset num_401_calls counter on redirects.""" | ||
219 | if r.is_redirect: | ||
220 | self._thread_local.num_401_calls = 1 | ||
221 | |||
222 | def handle_401(self, r, **kwargs): | ||
223 | """ | ||
224 | Takes the given response and tries digest-auth, if needed. | ||
225 | |||
226 | :rtype: requests.Response | ||
227 | """ | ||
228 | |||
229 | # If response is not 4xx, do not auth | ||
230 | # See https://github.com/requests/requests/issues/3772 | ||
231 | if not 400 <= r.status_code < 500: | ||
232 | self._thread_local.num_401_calls = 1 | ||
233 | return r | ||
234 | |||
235 | if self._thread_local.pos is not None: | ||
236 | # Rewind the file position indicator of the body to where | ||
237 | # it was to resend the request. | ||
238 | r.request.body.seek(self._thread_local.pos) | ||
239 | s_auth = r.headers.get('www-authenticate', '') | ||
240 | |||
241 | if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: | ||
242 | |||
243 | self._thread_local.num_401_calls += 1 | ||
244 | pat = re.compile(r'digest ', flags=re.IGNORECASE) | ||
245 | self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) | ||
246 | |||
247 | # Consume content and release the original connection | ||
248 | # to allow our new request to reuse the same one. | ||
249 | r.content | ||
250 | r.close() | ||
251 | prep = r.request.copy() | ||
252 | extract_cookies_to_jar(prep._cookies, r.request, r.raw) | ||
253 | prep.prepare_cookies(prep._cookies) | ||
254 | |||
255 | prep.headers['Authorization'] = self.build_digest_header( | ||
256 | prep.method, prep.url) | ||
257 | _r = r.connection.send(prep, **kwargs) | ||
258 | _r.history.append(r) | ||
259 | _r.request = prep | ||
260 | |||
261 | return _r | ||
262 | |||
263 | self._thread_local.num_401_calls = 1 | ||
264 | return r | ||
265 | |||
266 | def __call__(self, r): | ||
267 | # Initialize per-thread state, if needed | ||
268 | self.init_per_thread_state() | ||
269 | # If we have a saved nonce, skip the 401 | ||
270 | if self._thread_local.last_nonce: | ||
271 | r.headers['Authorization'] = self.build_digest_header(r.method, r.url) | ||
272 | try: | ||
273 | self._thread_local.pos = r.body.tell() | ||
274 | except AttributeError: | ||
275 | # In the case of HTTPDigestAuth being reused and the body of | ||
276 | # the previous request was a file-like object, pos has the | ||
277 | # file position of the previous body. Ensure it's set to | ||
278 | # None. | ||
279 | self._thread_local.pos = None | ||
280 | r.register_hook('response', self.handle_401) | ||
281 | r.register_hook('response', self.handle_redirect) | ||
282 | self._thread_local.num_401_calls = 1 | ||
283 | |||
284 | return r | ||
285 | |||
286 | def __eq__(self, other): | ||
287 | return all([ | ||
288 | self.username == getattr(other, 'username', None), | ||
289 | self.password == getattr(other, 'password', None) | ||
290 | ]) | ||
291 | |||
292 | def __ne__(self, other): | ||
293 | return not self == other | ||