From db5e58e49f9a74965214b8d3dcafa6a36fd6be12 Mon Sep 17 00:00:00 2001 From: kelson Date: Wed, 19 Aug 2015 11:25:00 -0400 Subject: [PATCH 1/3] added HttpOnly cookies (default) added unit tests for cookie layout, secure cookies, and HttpOnly cookies Session.httponly_cookies=False to revert HttpOnly cookies --- gluon/globals.py | 12 ++++++--- gluon/tests/test_globals.py | 54 ++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/gluon/globals.py b/gluon/globals.py index 644a6b83..3fbe42ba 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -1023,10 +1023,16 @@ class Session(Storage): def _fixup_before_save(self): response = current.response rcookies = response.cookies - if self._forget and response.session_id_name in rcookies: + scookies = rcookies.get(response.session_id_name) + if not scookies: + return + if self._forget: del rcookies[response.session_id_name] - elif self._secure and response.session_id_name in rcookies: - rcookies[response.session_id_name]['secure'] = True + return + if self.get('httponly_cookies',True): + scookies['HttpOnly'] = True + if self._secure: + scookies['secure'] = True def clear_session_cookies(self): request = current.request diff --git a/gluon/tests/test_globals.py b/gluon/tests/test_globals.py index 5f9d5526..8ba6d4f0 100644 --- a/gluon/tests/test_globals.py +++ b/gluon/tests/test_globals.py @@ -11,9 +11,23 @@ from fix_path import fix_sys_path fix_sys_path(__file__) -from gluon.globals import Response +from gluon.globals import Request, Response, Session from gluon import URL +def setup_clean_session(): + request = Request(env={}) + request.application = 'a' + request.controller = 'c' + request.function = 'f' + request.folder = 'applications/admin' + response = Response() + session = Session() + session.connect(request, response) + from gluon.globals import current + current.request = request + current.response = response + current.session = session + return current class testResponse(unittest.TestCase): @@ -120,5 +134,43 @@ class testResponse(unittest.TestCase): content = return_includes(response) self.assertEqual(content, '') + def test_cookies(self): + current = setup_clean_session() + cookie = str(current.response.cookies) + session_key='%s=%s'%(current.response.session_id_name,current.response.session_id) + self.assertRegexpMatches(cookie, r'^Set-Cookie: ') + self.assertTrue(session_key in cookie) + self.assertTrue('Path=/' in cookie) + + def test_cookies_secure(self): + current = setup_clean_session() + current.session._fixup_before_save() + cookie = str(current.response.cookies) + self.assertTrue('secure' not in cookie) + + current = setup_clean_session() + current.session.secure() + current.session._fixup_before_save() + cookie = str(current.response.cookies) + self.assertTrue('secure' in cookie) + + def test_cookies_httponly(self): + current = setup_clean_session() + current.session._fixup_before_save() + cookie = str(current.response.cookies) + self.assertTrue('httponly' in cookie) + + current = setup_clean_session() + current.session.httponly_cookies = True + current.session._fixup_before_save() + cookie = str(current.response.cookies) + self.assertTrue('httponly' in cookie) + + current = setup_clean_session() + current.session.httponly_cookies = False + current.session._fixup_before_save() + cookie = str(current.response.cookies) + self.assertTrue('httponly' not in cookie) + if __name__ == '__main__': unittest.main() From 0a79bf3afd3f9013af78e9d55ec0b73a16d5b000 Mon Sep 17 00:00:00 2001 From: kelson Date: Wed, 19 Aug 2015 11:30:07 -0400 Subject: [PATCH 2/3] assertRegexpMatches requires port for python2.5 and python2.6 --- gluon/tests/test_globals.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gluon/tests/test_globals.py b/gluon/tests/test_globals.py index 8ba6d4f0..a132985f 100644 --- a/gluon/tests/test_globals.py +++ b/gluon/tests/test_globals.py @@ -31,6 +31,17 @@ def setup_clean_session(): class testResponse(unittest.TestCase): + #port from python 2.7, needed for 2.5 and 2.6 tests + def assertRegexpMatches(self, text, expected_regexp, msg=None): + """Fail the test unless the text matches the regular expression.""" + if isinstance(expected_regexp, basestring): + expected_regexp = re.compile(expected_regexp) + if not expected_regexp.search(text): + msg = msg or "Regexp didn't match" + msg = '%s: %r not found in %r' % ( + msg, expected_regexp.pattern, text) + raise self.failureException(msg) + def test_include_files(self): def return_includes(response, extensions=None): From cbbd1246db5bd7208a814a6cee30a867540c2946 Mon Sep 17 00:00:00 2001 From: kelson Date: Wed, 19 Aug 2015 11:33:47 -0400 Subject: [PATCH 3/3] re import required for assertRegexpMatches port --- gluon/tests/test_globals.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gluon/tests/test_globals.py b/gluon/tests/test_globals.py index a132985f..90222bdc 100644 --- a/gluon/tests/test_globals.py +++ b/gluon/tests/test_globals.py @@ -6,6 +6,7 @@ """ +import re import unittest from fix_path import fix_sys_path