Add abstraction class to handle cookies
authorSimo Sorce <simo@redhat.com>
Tue, 16 Sep 2014 21:07:18 +0000 (17:07 -0400)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Wed, 24 Sep 2014 18:29:51 +0000 (20:29 +0200)
This handles secure cokies with useful helpers and defaults.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/login/common.py
ipsilon/util/cookies.py [new file with mode: 0755]
quickrun.py

index e59085f..f2254c9 100755 (executable)
@@ -23,6 +23,7 @@ from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginLoader, PluginObject
 from ipsilon.util.plugin import PluginInstaller
 from ipsilon.info.common import Info
 from ipsilon.util.plugin import PluginLoader, PluginObject
 from ipsilon.util.plugin import PluginInstaller
 from ipsilon.info.common import Info
+from ipsilon.util.cookies import SecureCookie
 import cherrypy
 
 
 import cherrypy
 
 
@@ -67,13 +68,10 @@ class LoginManagerBase(PluginObject, Log):
 
         # save username into a cookie if parent was form base auth
         if auth_type == 'password':
 
         # save username into a cookie if parent was form base auth
         if auth_type == 'password':
-            cherrypy.response.cookie[USERNAME_COOKIE] = username
-            cherrypy.response.cookie[USERNAME_COOKIE]['path'] = \
-                cherrypy.config.get('base.mount', '/')
-            cherrypy.response.cookie[USERNAME_COOKIE]['secure'] = True
-            cherrypy.response.cookie[USERNAME_COOKIE]['httponly'] = True
+            cookie = SecureCookie(USERNAME_COOKIE, username)
             # 15 days
             # 15 days
-            cherrypy.response.cookie[USERNAME_COOKIE]['max-age'] = 1296000
+            cookie.maxage = 1296000
+            cookie.send()
 
         raise cherrypy.HTTPRedirect(ref)
 
 
         raise cherrypy.HTTPRedirect(ref)
 
@@ -180,9 +178,11 @@ class LoginFormBase(LoginPageBase):
         if self.lm.next_login is not None:
             next_url = self.lm.next_login.path
 
         if self.lm.next_login is not None:
             next_url = self.lm.next_login.path
 
-        username = ''
-        if USERNAME_COOKIE in cherrypy.request.cookie:
-            username = cherrypy.request.cookie[USERNAME_COOKIE].value
+        cookie = SecureCookie(USERNAME_COOKIE)
+        cookie.receive()
+        username = cookie.value
+        if username is None:
+            username = ''
 
         context = {
             "title": 'Login',
 
         context = {
             "title": 'Login',
diff --git a/ipsilon/util/cookies.py b/ipsilon/util/cookies.py
new file mode 100755 (executable)
index 0000000..cd68242
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+from ipsilon.util.log import Log
+import cherrypy
+import uuid
+
+
+class SecureCookie(Log):
+
+    def __init__(self, name=None, value=None, maxage=None, expires=None):
+        if name is None:
+            self.name = str(uuid.uuid4())
+        else:
+            self.name = str(name)
+        self.path = None
+        self.secure = cherrypy.config.get('tools.sessions.secure', True)
+        self.httponly = cherrypy.config.get('tools.sessions.httponly', True)
+        self.maxage = maxage
+        self.expires = expires
+        self.value = value
+
+    def _get_cookie_attr(self, name):
+        return getattr(cherrypy.request.cookie[self.name], name, None)
+
+    def _set_cookie_attr(self, name, value):
+        if value is not None and value is not False:
+            cherrypy.response.cookie[self.name][name] = value
+
+    def receive(self):
+        if self.name not in cherrypy.request.cookie:
+            return
+
+        self.value = cherrypy.request.cookie[self.name].value
+        self.path = self._get_cookie_attr('path')
+        self.secure = self._get_cookie_attr('secure')
+        self.httponly = self._get_cookie_attr('httponly')
+        self.maxage = self._get_cookie_attr('max-age')
+        self.expires = self._get_cookie_attr('expires')
+
+    def _store(self):
+        if self.value is None:
+            raise ValueError('Cookie has no value')
+        if self.maxage is None and self.expires is not 0:
+            # 5 minutes should be enough ...
+            self.maxage = 300
+        cherrypy.response.cookie[self.name] = str(self.value)
+        if self.path:
+            path = self.path
+        else:
+            path = cherrypy.config.get('base.mount', '/')
+        self._set_cookie_attr('path', path)
+        self._set_cookie_attr('secure', self.secure)
+        self._set_cookie_attr('httponly', self.httponly)
+        self._set_cookie_attr('max-age', self.maxage)
+        self._set_cookie_attr('expires', self.expires)
+        self.debug('Cookie op: %s' % cherrypy.response.cookie[self.name])
+
+    def delete(self):
+        self.expires = 0
+        self.debug('Deleting cookie %s' % self.name)
+        self._store()
+
+    def send(self):
+        self.debug('Sending cookie %s' % self.name)
+        self._store()
index 5ec6e53..8157f80 100755 (executable)
@@ -48,6 +48,8 @@ tools.sessions.on = True
 tools.sessions.storage_type = "file"
 tools.sessions.storage_path = "${WORKDIR}/sessions"
 tools.sessions.timeout = 60
 tools.sessions.storage_type = "file"
 tools.sessions.storage_path = "${WORKDIR}/sessions"
 tools.sessions.timeout = 60
+tools.sessions.secure = False
+tools.sessions.httponly = False
 '''
 
 ADMIN_TEMPLATE='''
 '''
 
 ADMIN_TEMPLATE='''