Skip to content

Commit

Permalink
enhanced cookie support (#1010)
Browse files Browse the repository at this point in the history
* enhanced cookie support

* Add change log entry

* Apply suggestions from code review

Apply @dateflake suggestions

Co-authored-by: Jens Vagelpohl <jens@plyp.com>

* Apply suggestions from @dataflake

* avoid unicode result of `domain_converter`

Co-authored-by: Jens Vagelpohl <jens@plyp.com>
  • Loading branch information
d-maurer and dataflake committed Feb 7, 2022
1 parent a19ad09 commit 12c78aa
Show file tree
Hide file tree
Showing 10 changed files with 703 additions and 101 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
4.7.1 (unreleased)
------------------

- Enhance cookie support. For details, see
`#1010 <https://github.com/zopefoundation/Zope/issues/1010>`_

- Update dependencies to the latest releases that still support Python 2.


Expand Down
2 changes: 1 addition & 1 deletion src/Testing/ZopeTestCase/testFunctional.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def testCookie(self):
response = self.publish(self.folder_path + '/set_cookie')
self.assertEqual(response.getStatus(), 200)
self.assertEqual(response.getCookie('foo').get('value'), 'Bar')
self.assertEqual(response.getCookie('foo').get('path'), '/')
self.assertEqual(response.getCookie('foo').get('Path'), '/')

def testChangeTitle(self):
# Change the title of a document
Expand Down
2 changes: 1 addition & 1 deletion src/Testing/ZopeTestCase/zopedoctest/FunctionalDocTest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Test setting cookies
>>> response.status
200

>>> b'Set-Cookie: foo="Bar"; Path=/' in response.getOutput()
>>> b'Set-Cookie: foo=Bar; Path=/' in response.getOutput()
True

Test reading cookies
Expand Down
2 changes: 1 addition & 1 deletion src/Testing/tests/test_testbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def test_cookies(self):

browser = Browser()
browser.open('http://localhost/test_folder_1_/stub')
self.assertEqual(browser.cookies.get('evil'), '"cookie"')
self.assertEqual(browser.cookies.get('evil'), 'cookie')

def test_handle_errors_true(self):
self.folder._setObject('stub', ExceptionStub())
Expand Down
4 changes: 3 additions & 1 deletion src/ZPublisher/HTTPRequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
from ZPublisher.interfaces import IXmlrpcChecker
from ZPublisher.utils import basic_auth_decode

from .cookie import getCookieValuePolicy


if PY3:
from html import escape
Expand Down Expand Up @@ -1769,7 +1771,7 @@ def parse_cookie(text,
return result

if name not in result:
result[name] = unquote(value)
result[name] = getCookieValuePolicy().load(name, value)

return parse_cookie(text[c_len:], result)

Expand Down
56 changes: 19 additions & 37 deletions src/ZPublisher/HTTPResponse.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
from ZPublisher.Iterators import IStreamIterator
from ZPublisher.Iterators import IUnboundStreamIterator

from .cookie import convertCookieParameter
from .cookie import getCookieParamPolicy
from .cookie import getCookieValuePolicy


try:
from html import escape
Expand Down Expand Up @@ -310,9 +314,12 @@ def setCookie(self, name, value, quoted=True, **kw):
else:
cookie = cookies[name] = {}
for k, v in kw.items():
k, v = convertCookieParameter(k, v)
cookie[k] = v
cookie['value'] = value
cookie['quoted'] = quoted
# RFC6265 makes quoting obsolete
# cookie['quoted'] = quoted
getCookieParamPolicy().check_consistency(name, cookie)

def appendCookie(self, name, value):
""" Set an HTTP cookie.
Expand Down Expand Up @@ -676,44 +683,19 @@ def _encode_unicode(self, text):

def _cookie_list(self):
cookie_list = []
dump = getCookieValuePolicy().dump
parameters = getCookieParamPolicy().parameters
for name, attrs in self.cookies.items():

# Note that as of May 98, IE4 ignores cookies with
# quoted cookie attr values, so only the value part
# of name=value pairs may be quoted.

if attrs.get('quoted', True):
cookie = '%s="%s"' % (name, quote(attrs['value']))
else:
cookie = '%s=%s' % (name, quote(attrs['value']))
for name, v in attrs.items():
name = name.lower()
if name == 'expires':
cookie = '%s; Expires=%s' % (cookie, v)
elif name == 'domain':
cookie = '%s; Domain=%s' % (cookie, v)
elif name == 'path':
cookie = '%s; Path=%s' % (cookie, v)
elif name == 'max_age':
cookie = '%s; Max-Age=%s' % (cookie, v)
elif name == 'comment':
cookie = '%s; Comment=%s' % (cookie, v)
elif name == 'secure' and v:
cookie = '%s; Secure' % cookie
# Some browsers recognize this cookie attribute
# and block read/write access via JavaScript
elif name == 'http_only' and v:
cookie = '%s; HTTPOnly' % cookie
# Some browsers recognize the SameSite cookie attribute
# and do not send the cookie along with cross-site requests
# providing some protection against CSRF attacks
# https://tools.ietf.org/html/draft-west-first-party-cookies-07
elif name == 'same_site' and v:
cookie = '%s; SameSite=%s' % (cookie, v)
params = []
for pn, pv in parameters(name, attrs):
if pv is None:
continue
params.append(pn if pv is True else "%s=%s" % (pn, pv))
cookie = "%s=%s" % (name, dump(name, attrs["value"]))
if params:
cookie += "; " + "; ".join(params)
# Should really check size of cookies here!
cookie_list.append(('Set-Cookie', cookie))

# Should really check size of cookies here!

return cookie_list

def listHeaders(self):
Expand Down

0 comments on commit 12c78aa

Please sign in to comment.