From 3501ea463432400c77479dddedab3b7af967b618 Mon Sep 17 00:00:00 2001 From: daftshady Date: Thu, 26 Sep 2013 15:40:12 +0900 Subject: [PATCH 1/7] Fixed #1623. Added 'MD5-sess' algorithm to HTTPDigestAuth --- requests/auth.py | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/requests/auth.py b/requests/auth.py index 30529e29..e11a1fa9 100644 --- a/requests/auth.py +++ b/requests/auth.py @@ -77,7 +77,7 @@ class HTTPDigestAuth(AuthBase): else: _algorithm = algorithm.upper() # lambdas assume digest modules are imported at the top level - if _algorithm == 'MD5': + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': def md5_utf8(x): if isinstance(x, str): x = x.encode('utf-8') @@ -89,7 +89,7 @@ class HTTPDigestAuth(AuthBase): x = x.encode('utf-8') return hashlib.sha1(x).hexdigest() hash_utf8 = sha_utf8 - # XXX MD5-sess + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) if hash_utf8 is None: @@ -104,24 +104,29 @@ class HTTPDigestAuth(AuthBase): A1 = '%s:%s:%s' % (self.username, realm, self.password) A2 = '%s:%s' % (method, path) + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self.last_nonce: + self.nonce_count += 1 + else: + self.nonce_count = 1 + ncvalue = '%08x' % self.nonce_count + s = str(self.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, HA2) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) if qop is None: - respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2))) + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) elif qop == 'auth' or 'auth' in qop.split(','): - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - - ncvalue = '%08x' % self.nonce_count - s = str(self.nonce_count).encode('utf-8') - s += nonce.encode('utf-8') - s += time.ctime().encode('utf-8') - s += os.urandom(8) - - cnonce = (hashlib.sha1(s).hexdigest()[:16]) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2)) - respdig = KD(hash_utf8(A1), noncebit) + respdig = KD(HA1, noncebit) else: # XXX handle auth-int. return None From 1d1831ba40fe53a37e5ab87e9913dd1c52349066 Mon Sep 17 00:00:00 2001 From: daftshady Date: Fri, 27 Sep 2013 22:17:40 +0900 Subject: [PATCH 2/7] fixed extra bonus bug --- requests/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/models.py b/requests/models.py index 8fd97353..3f15cc1f 100644 --- a/requests/models.py +++ b/requests/models.py @@ -295,7 +295,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): p = PreparedRequest() p.method = self.method p.url = self.url - p.headers = self.headers + p.headers = self.headers.copy() p.body = self.body p.hooks = self.hooks return p From 39fe0068aa657764778782ce4ca11d5e3ee08c66 Mon Sep 17 00:00:00 2001 From: daftshady Date: Sat, 28 Sep 2013 16:33:05 +0900 Subject: [PATCH 3/7] Fixed #1630, added test case for cookie persistence. --- requests/sessions.py | 7 +++++++ test_requests.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/requests/sessions.py b/requests/sessions.py index aa956d3f..a7e6d4e7 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -146,6 +146,13 @@ class SessionRedirectMixin(object): extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + # Restore original cookies in redirects response. + headers = resp.request.headers + try: + headers['Cookie'] = req.headers['Cookie'] + except KeyError: + pass + i += 1 yield resp diff --git a/test_requests.py b/test_requests.py index d1e6ed0a..3d6665ba 100755 --- a/test_requests.py +++ b/test_requests.py @@ -164,6 +164,12 @@ class RequestsTestCase(unittest.TestCase): s.get(httpbin('cookies/set?foo="bar:baz"')) self.assertTrue(s.cookies['foo'] == '"bar:baz"') + def test_cookie_persists_via_api(self): + s = requests.session() + r = s.get(httpbin('redirect/1'), cookies={'foo':'bar'}) + self.assertTrue('foo' in r.request.headers['Cookie']) + self.assertTrue('foo' in r.history[0].request.headers['Cookie']) + def test_request_cookie_overrides_session_cookie(self): s = requests.session() s.cookies['foo'] = 'bar' From 373166d833aa5e89d38859f11be59ad3aaba5a1d Mon Sep 17 00:00:00 2001 From: daftshady Date: Sat, 28 Sep 2013 16:41:08 +0900 Subject: [PATCH 4/7] checkout auth --- requests/auth.py | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/requests/auth.py b/requests/auth.py index e11a1fa9..30529e29 100644 --- a/requests/auth.py +++ b/requests/auth.py @@ -77,7 +77,7 @@ class HTTPDigestAuth(AuthBase): else: _algorithm = algorithm.upper() # lambdas assume digest modules are imported at the top level - if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': + if _algorithm == 'MD5': def md5_utf8(x): if isinstance(x, str): x = x.encode('utf-8') @@ -89,7 +89,7 @@ class HTTPDigestAuth(AuthBase): x = x.encode('utf-8') return hashlib.sha1(x).hexdigest() hash_utf8 = sha_utf8 - + # XXX MD5-sess KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) if hash_utf8 is None: @@ -104,29 +104,24 @@ class HTTPDigestAuth(AuthBase): A1 = '%s:%s:%s' % (self.username, realm, self.password) A2 = '%s:%s' % (method, path) - - HA1 = hash_utf8(A1) - HA2 = hash_utf8(A2) - - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - ncvalue = '%08x' % self.nonce_count - s = str(self.nonce_count).encode('utf-8') - s += nonce.encode('utf-8') - s += time.ctime().encode('utf-8') - s += os.urandom(8) - - cnonce = (hashlib.sha1(s).hexdigest()[:16]) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, HA2) - if _algorithm == 'MD5-SESS': - HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) if qop is None: - respdig = KD(HA1, "%s:%s" % (nonce, HA2)) + respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2))) elif qop == 'auth' or 'auth' in qop.split(','): - respdig = KD(HA1, noncebit) + if nonce == self.last_nonce: + self.nonce_count += 1 + else: + self.nonce_count = 1 + + ncvalue = '%08x' % self.nonce_count + s = str(self.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2)) + respdig = KD(hash_utf8(A1), noncebit) else: # XXX handle auth-int. return None From d22eaf96e2a1c64bc29579a072f67d2a9c758ef8 Mon Sep 17 00:00:00 2001 From: daftshady Date: Sat, 28 Sep 2013 23:44:27 +0900 Subject: [PATCH 5/7] Fixed #1630 --- requests/cookies.py | 16 ++++++++++++++++ requests/sessions.py | 12 ++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index f3ac64f0..95157e7d 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -404,3 +404,19 @@ def cookiejar_from_dict(cookie_dict, cookiejar=None): for name in cookie_dict: cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) return cookiejar + + +def merge_session_cookies(cookiejar, cookie_dict): + """Merges cookie_dict with session CookieJar. + + :param cookiejar: Should be session cookie. + :param cookie_dict: Dict of key/values to be merged. + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + for k in cookie_dict: + # Session should not be modified by request cookie. + if k not in [cookie.name for cookie in cookiejar]: + cookiejar.set_cookie(create_cookie(k, cookie_dict[k])) diff --git a/requests/sessions.py b/requests/sessions.py index a7e6d4e7..b917acc8 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -13,7 +13,7 @@ from collections import Mapping from datetime import datetime from .compat import cookielib, OrderedDict, urljoin, urlparse -from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar +from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_session_cookies from .models import Request, PreparedRequest from .hooks import default_hooks, dispatch_hook from .utils import to_key_val_list, default_headers @@ -146,13 +146,6 @@ class SessionRedirectMixin(object): extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) - # Restore original cookies in redirects response. - headers = resp.request.headers - try: - headers['Cookie'] = req.headers['Cookie'] - except KeyError: - pass - i += 1 yield resp @@ -329,6 +322,9 @@ class Session(SessionRedirectMixin): ) prep = self.prepare_request(req) + # Add param cookies to session cookies + merge_session_cookies(self.cookies, cookies) + proxies = proxies or {} # Gather clues from the surrounding environment. From 012f0334ce43fe23044fc58e4246a804db88650d Mon Sep 17 00:00:00 2001 From: daftshady Date: Sun, 29 Sep 2013 01:08:58 +0900 Subject: [PATCH 6/7] added param to cookiejar_from_dict instead of creating new method. --- requests/cookies.py | 26 ++++++++------------------ requests/sessions.py | 4 ++-- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index 95157e7d..ea56c065 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -392,31 +392,21 @@ def morsel_to_cookie(morsel): return c -def cookiejar_from_dict(cookie_dict, cookiejar=None): +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): """Returns a CookieJar from a key/value dictionary. :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. """ if cookiejar is None: cookiejar = RequestsCookieJar() if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] for name in cookie_dict: - cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + return cookiejar - - -def merge_session_cookies(cookiejar, cookie_dict): - """Merges cookie_dict with session CookieJar. - - :param cookiejar: Should be session cookie. - :param cookie_dict: Dict of key/values to be merged. - """ - if cookiejar is None: - cookiejar = RequestsCookieJar() - - if cookie_dict is not None: - for k in cookie_dict: - # Session should not be modified by request cookie. - if k not in [cookie.name for cookie in cookiejar]: - cookiejar.set_cookie(create_cookie(k, cookie_dict[k])) diff --git a/requests/sessions.py b/requests/sessions.py index b917acc8..eda9f92d 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -13,7 +13,7 @@ from collections import Mapping from datetime import datetime from .compat import cookielib, OrderedDict, urljoin, urlparse -from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_session_cookies +from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar from .models import Request, PreparedRequest from .hooks import default_hooks, dispatch_hook from .utils import to_key_val_list, default_headers @@ -323,7 +323,7 @@ class Session(SessionRedirectMixin): prep = self.prepare_request(req) # Add param cookies to session cookies - merge_session_cookies(self.cookies, cookies) + self.cookies = cookiejar_from_dict(cookies, cookiejar=self.cookies, overwrite=False) proxies = proxies or {} From c8fc19a7799f34915a9a9e1ccfa2cb99ecd404da Mon Sep 17 00:00:00 2001 From: daftshady Date: Sun, 29 Sep 2013 02:27:20 +0900 Subject: [PATCH 7/7] add myself to authors. --- AUTHORS.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 43a4e4df..072321bb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -135,4 +135,5 @@ Patches and Suggestions - David Pursehouse @dpursehouse - Jon Parise - Alexander Karpinsky @homm86 -- Marc Schlaich @schlamar \ No newline at end of file +- Marc Schlaich @schlamar +- Park Ilsu @daftshady