[web] add support for HttpOnly cookie flag
authorJulien Cristau <julien.cristau@logilab.fr>
Tue, 15 Jul 2014 16:07:59 +0200
changeset 10001 1245357b3b3e
parent 10000 4352b7ccde04
child 10002 586d0e527052
[web] add support for HttpOnly cookie flag And use it for session cookies. Closes #4142521.
devtools/fake.py
web/application.py
web/http_headers.py
web/request.py
web/test/unittest_web.py
--- a/devtools/fake.py	Fri Oct 17 18:16:58 2014 +0200
+++ b/devtools/fake.py	Tue Jul 15 16:07:59 2014 +0200
@@ -65,8 +65,8 @@
         super(FakeRequest, self).__init__(*args, **kwargs)
         self._session_data = {}
 
-    def set_cookie(self, name, value, maxage=300, expires=None, secure=False):
-        super(FakeRequest, self).set_cookie(name, value, maxage, expires, secure)
+    def set_cookie(self, name, value, maxage=300, expires=None, secure=False, httponly=False):
+        super(FakeRequest, self).set_cookie(name, value, maxage, expires, secure, httponly)
         cookie = self.get_response_header('Set-Cookie')
         self._headers_in.setHeader('Cookie', cookie)
 
--- a/web/application.py	Fri Oct 17 18:16:58 2014 +0200
+++ b/web/application.py	Tue Jul 15 16:07:59 2014 +0200
@@ -224,7 +224,7 @@
         sessioncookie = self.session_cookie(req)
         secure = req.https and req.base_url().startswith('https://')
         req.set_cookie(sessioncookie, session.sessionid,
-                       maxage=None, secure=secure)
+                       maxage=None, secure=secure, httponly=True)
         if not session.anonymous_session:
             self.session_manager.postlogin(req, session)
         return session
--- a/web/http_headers.py	Fri Oct 17 18:16:58 2014 +0200
+++ b/web/http_headers.py	Tue Jul 15 16:07:59 2014 +0200
@@ -934,9 +934,13 @@
 
 #### Cookies. Blech!
 class Cookie(object):
-    # __slots__ = ['name', 'value', 'path', 'domain', 'ports', 'expires', 'discard', 'secure', 'comment', 'commenturl', 'version']
+    # __slots__ = ['name', 'value', 'path', 'domain', 'ports', 'expires',
+    #              'discard', 'secure', 'httponly', 'comment', 'commenturl',
+    #              'version']
 
-    def __init__(self, name, value, path=None, domain=None, ports=None, expires=None, discard=False, secure=False, comment=None, commenturl=None, version=0):
+    def __init__(self, name, value, path=None, domain=None, ports=None,
+                 expires=None, discard=False, secure=False, httponly=False,
+                 comment=None, commenturl=None, version=0):
         self.name = name
         self.value = value
         self.path = path
@@ -945,6 +949,7 @@
         self.expires = expires
         self.discard = discard
         self.secure = secure
+        self.httponly = httponly
         self.comment = comment
         self.commenturl = commenturl
         self.version = version
@@ -955,7 +960,8 @@
         if self.domain is not None: s+=", domain=%r" % (self.domain,)
         if self.ports is not None: s+=", ports=%r" % (self.ports,)
         if self.expires is not None: s+=", expires=%r" % (self.expires,)
-        if self.secure is not False: s+=", secure=%r" % (self.secure,)
+        if self.secure: s+=", secure"
+        if self.httponly: s+=", HttpOnly"
         if self.comment is not None: s+=", comment=%r" % (self.comment,)
         if self.commenturl is not None: s+=", commenturl=%r" % (self.commenturl,)
         if self.version != 0: s+=", version=%r" % (self.version,)
@@ -1213,6 +1219,8 @@
             out.append("domain=%s" % cookie.domain)
         if cookie.secure:
             out.append("secure")
+        if cookie.httponly:
+            out.append("HttpOnly")
 
         setCookies.append('; '.join(out))
     return setCookies
@@ -1240,6 +1248,8 @@
                 out.append("Port=%s" % quoteString(",".join([str(x) for x in cookie.ports])))
         if cookie.secure:
             out.append("Secure")
+        if cookie.httponly:
+            out.append("HttpOnly")
         out.append('Version="1"')
         setCookies.append('; '.join(out))
     return setCookies
--- a/web/request.py	Fri Oct 17 18:16:58 2014 +0200
+++ b/web/request.py	Tue Jul 15 16:07:59 2014 +0200
@@ -569,7 +569,7 @@
         except KeyError:
             return SimpleCookie()
 
-    def set_cookie(self, name, value, maxage=300, expires=None, secure=False):
+    def set_cookie(self, name, value, maxage=300, expires=None, secure=False, httponly=False):
         """set / update a cookie
 
         by default, cookie will be available for the next 5 minutes.
@@ -595,7 +595,7 @@
             expires = None
         # make sure cookie is set on the correct path
         cookie = Cookie(str(name), str(value), self.base_url_path(),
-                        expires=expires, secure=secure)
+                        expires=expires, secure=secure, httponly=httponly)
         self.headers_out.addHeader('Set-cookie', cookie)
 
     def remove_cookie(self, name, bwcompat=None):
--- a/web/test/unittest_web.py	Fri Oct 17 18:16:58 2014 +0200
+++ b/web/test/unittest_web.py	Tue Jul 15 16:07:59 2014 +0200
@@ -117,6 +117,10 @@
         webreq = self.web_request('/%d' % admin_eid)
         self.assertEqual(webreq.status, 200)
 
+    def test_session_cookie_httponly(self):
+        webreq = self.web_request()
+        self.assertIn('HttpOnly', webreq.getheader('set-cookie'))
+
 
 class LogQueriesTC(CubicWebServerTC):
     @classmethod