Commit 33444d0e authored by Tres Seaver's avatar Tres Seaver

Extended BrowserIdManager to expose the 'HTTPOnly' attribute for its cookie.

o Via https://bugs.launchpad.net/zope2/+bug/367393 
parent fc6779e8
...@@ -21,6 +21,9 @@ Restructuring ...@@ -21,6 +21,9 @@ Restructuring
Features Added Features Added
++++++++++++++ ++++++++++++++
- Extended BrowserIdManager to expose the 'HTTPOnly' attribute for its
cookie. Also via https://bugs.launchpad.net/zope2/+bug/367393 .
- Addeed support for an optional 'HTTPOnly' attribute of cookies (see - Addeed support for an optional 'HTTPOnly' attribute of cookies (see
http://www.owasp.org/index.php/HTTPOnly). Patch from Stephan Hofmockel, http://www.owasp.org/index.php/HTTPOnly). Patch from Stephan Hofmockel,
via https://bugs.launchpad.net/zope2/+bug/367393 . via https://bugs.launchpad.net/zope2/+bug/367393 .
......
...@@ -70,12 +70,13 @@ LOG = logging.getLogger('Zope.BrowserIdManager') ...@@ -70,12 +70,13 @@ LOG = logging.getLogger('Zope.BrowserIdManager')
def constructBrowserIdManager( def constructBrowserIdManager(
self, id=BROWSERID_MANAGER_NAME, title='', idname='_ZopeId', self, id=BROWSERID_MANAGER_NAME, title='', idname='_ZopeId',
location=('cookies', 'form'), cookiepath='/', cookiedomain='', location=('cookies', 'form'), cookiepath='/', cookiedomain='',
cookielifedays=0, cookiesecure=0, auto_url_encoding=0, REQUEST=None cookielifedays=0, cookiesecure=0, cookiehttponly=0, auto_url_encoding=0,
REQUEST=None
): ):
""" """ """ """
ob = BrowserIdManager(id, title, idname, location, cookiepath, ob = BrowserIdManager(id, title, idname, location, cookiepath,
cookiedomain, cookielifedays, cookiesecure, cookiedomain, cookielifedays, cookiesecure,
auto_url_encoding) cookiehttponly, auto_url_encoding)
self._setObject(id, ob) self._setObject(id, ob)
ob = self._getOb(id) ob = self._getOb(id)
if REQUEST is not None: if REQUEST is not None:
...@@ -115,7 +116,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -115,7 +116,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
def __init__(self, id, title='', idname='_ZopeId', def __init__(self, id, title='', idname='_ZopeId',
location=('cookies', 'form'), cookiepath=('/'), location=('cookies', 'form'), cookiepath=('/'),
cookiedomain='', cookielifedays=0, cookiesecure=0, cookiedomain='', cookielifedays=0, cookiesecure=0,
auto_url_encoding=0): cookiehttponly=0, auto_url_encoding=0):
self.id = str(id) self.id = str(id)
self.title = str(title) self.title = str(title)
self.setBrowserIdName(idname) self.setBrowserIdName(idname)
...@@ -124,6 +125,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -124,6 +125,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
self.setCookieDomain(cookiedomain) self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays) self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure) self.setCookieSecure(cookiesecure)
self.setCookieHTTPOnly(cookiehttponly)
self.setAutoUrlEncoding(auto_url_encoding) self.setAutoUrlEncoding(auto_url_encoding)
def manage_afterAdd(self, item, container): def manage_afterAdd(self, item, container):
...@@ -278,7 +280,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -278,7 +280,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
def manage_changeBrowserIdManager( def manage_changeBrowserIdManager(
self, title='', idname='_ZopeId', location=('cookies', 'form'), self, title='', idname='_ZopeId', location=('cookies', 'form'),
cookiepath='/', cookiedomain='', cookielifedays=0, cookiesecure=0, cookiepath='/', cookiedomain='', cookielifedays=0, cookiesecure=0,
auto_url_encoding=0, REQUEST=None cookiehttponly=0, auto_url_encoding=0, REQUEST=None
): ):
""" """ """ """
self.title = str(title) self.title = str(title)
...@@ -287,6 +289,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -287,6 +289,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
self.setCookieDomain(cookiedomain) self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays) self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure) self.setCookieSecure(cookiesecure)
self.setCookieHTTPOnly(cookiehttponly)
self.setBrowserIdNamespaces(location) self.setBrowserIdNamespaces(location)
self.setAutoUrlEncoding(auto_url_encoding) self.setAutoUrlEncoding(auto_url_encoding)
self.updateTraversalData() self.updateTraversalData()
...@@ -377,6 +380,16 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -377,6 +380,16 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" """ """ """
return self.cookie_domain return self.cookie_domain
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieHTTPOnly')
def setCookieHTTPOnly(self, http_only):
""" sets cookie 'HTTPOnly' on or off """
self.cookie_http_only = bool(http_only)
security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookieHTTPOnly')
def getCookieHTTPOnly(self):
""" retrieve the 'HTTPOnly' flag """
return self.cookie_http_only
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure') security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure')
def setCookieSecure(self, secure): def setCookieSecure(self, secure):
""" sets cookie 'secure' element for id cookie """ """ sets cookie 'secure' element for id cookie """
...@@ -387,7 +400,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -387,7 +400,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" """ """ """
return self.cookie_secure return self.cookie_secure
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure') security.declareProtected(CHANGE_IDMGR_PERM, 'setAutoUrlEncoding')
def setAutoUrlEncoding(self, auto_url_encoding): def setAutoUrlEncoding(self, auto_url_encoding):
""" sets 'auto url encoding' on or off """ """ sets 'auto url encoding' on or off """
self.auto_url_encoding = not not auto_url_encoding self.auto_url_encoding = not not auto_url_encoding
...@@ -424,8 +437,11 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -424,8 +437,11 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
expires = now() + self.cookie_life_days * 86400 expires = now() + self.cookie_life_days * 86400
# Wdy, DD-Mon-YYYY HH:MM:SS GMT # Wdy, DD-Mon-YYYY HH:MM:SS GMT
expires = strftime('%a %d-%b-%Y %H:%M:%S GMT',gmtime(expires)) expires = strftime('%a %d-%b-%Y %H:%M:%S GMT',gmtime(expires))
# cookie attributes managed by BrowserIdManager
d = {'domain':self.cookie_domain,'path':self.cookie_path, d = {'domain':self.cookie_domain,'path':self.cookie_path,
'secure':self.cookie_secure,'expires':expires} 'secure':self.cookie_secure,'http_only': self.cookie_http_only,
'expires':expires}
if self.cookie_secure: if self.cookie_secure:
URL1 = REQUEST.get('URL1', None) URL1 = REQUEST.get('URL1', None)
......
...@@ -150,6 +150,16 @@ by interacting with the Zope sessioning machinery. ...@@ -150,6 +150,16 @@ by interacting with the Zope sessioning machinery.
<INPUT TYPE="checkbox" NAME="cookiesecure"> <INPUT TYPE="checkbox" NAME="cookiesecure">
</TD> </TD>
</TR> </TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
Make cookie not aviable from JavaScript
</div>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="checkbox" NAME="cookiehttponly">
</TD>
</TR>
</TR> <TR> <TD></TD> <TD> </TR> <TR> <TD></TD> <TD>
......
...@@ -134,6 +134,19 @@ ...@@ -134,6 +134,19 @@
<dtml-if getCookieSecure>CHECKED</dtml-if>> <dtml-if getCookieSecure>CHECKED</dtml-if>>
</TD> </TD>
</TR> </TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
Make cookie not aviable from JavaScript
</div>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="checkbox" NAME="cookiehttponly"
<dtml-if getCookieHTTPOnly>CHECKED</dtml-if>>
</TD>
</TR>
<TR> <TR>
<TD></TD> <TD></TD>
<TD><BR><INPUT class="form-element" TYPE="SUBMIT" VALUE=" Change "></TD> <TD><BR><INPUT class="form-element" TYPE="SUBMIT" VALUE=" Change "></TD>
...@@ -141,4 +154,3 @@ ...@@ -141,4 +154,3 @@
</TABLE> </TABLE>
</FORM> </FORM>
<dtml-var manage_page_footer> <dtml-var manage_page_footer>
...@@ -116,6 +116,10 @@ class TestBrowserIdManager(TestCase): ...@@ -116,6 +116,10 @@ class TestBrowserIdManager(TestCase):
self.m.setCookieSecure(1) self.m.setCookieSecure(1)
self.failUnless( self.m.getCookieSecure() == 1 ) self.failUnless( self.m.getCookieSecure() == 1 )
def testSetCookieHTTPOnly(self):
self.m.setCookieHTTPOnly(True)
self.assertEqual( self.m.getCookieHTTPOnly(), True )
def testGetBrowserIdCookie(self): def testGetBrowserIdCookie(self):
token = self.m.getBrowserId() token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token self.m.REQUEST.browser_id_ = token
...@@ -223,6 +227,29 @@ class TestBrowserIdManager(TestCase): ...@@ -223,6 +227,29 @@ class TestBrowserIdManager(TestCase):
(keystring, key)) (keystring, key))
self.failUnless( html == expected ) self.failUnless( html == expected )
def testHTTPOnlyCookieAttribute(self):
self.m.setCookieHTTPOnly(True)
self.failUnless(self.m.getBrowserId())
resp_cookies = self.req.RESPONSE.cookies
session_cookie = resp_cookies[self.m.browserid_name]
self.assertEqual(session_cookie['http_only'], True)
def testSecureCookieAttribute_correct_url(self):
self.m.setCookieSecure(1)
self.req['URL1'] = 'https://www.test.org'
self.failUnless(self.m.getBrowserId())
resp_cookies = self.req.RESPONSE.cookies
session_cookie = resp_cookies[self.m.browserid_name]
self.assertEqual(session_cookie['secure'], True)
# This test document the 'feature':
# return a browser ID but dont set the cookie
def testSecureCookieAttribute_wrong_url(self):
self.m.setCookieSecure(1)
self.req['URL1'] = 'http://www.test.org'
self.failUnless(self.m.getBrowserId())
self.assertEqual( self.req.RESPONSE.cookies, {} )
def testAutoUrlEncoding(self): def testAutoUrlEncoding(self):
self.m.setAutoUrlEncoding(1) self.m.setAutoUrlEncoding(1)
self.m.setBrowserIdNamespaces(('url',)) self.m.setBrowserIdNamespaces(('url',))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment