Robert Brewer avatar Robert Brewer committed 63ead7b

Fix for #710 (Allow forcing a new session id).

Comments (0)

Files changed (3)

cherrypy/_cptools.py

         
         hooks.attach('before_finalize', _sessions.save)
         hooks.attach('on_end_request', _sessions.close)
+        
+    def regenerate(self):
+        """Drop the current session and make a new one (with a new id)."""
+        sess = cherrypy.serving.session
+        sess.regenerate()
+        
+        # Grab cookie-relevant tool args
+        conf = dict([(k, v) for k, v in self._merged_args().iteritems()
+                     if k in ('path', 'path_header', 'name', 'timeout',
+                              'domain', 'secure')])
+        _sessions.set_response_cookie(**conf)
+
+
 
 
 class XMLRPCController(object):

cherrypy/lib/sessions.py

         for k, v in kwargs.iteritems():
             setattr(self, k, v)
         
-        self.id = id
+        if id is None:
+            self.regenerate()
+        else:
+            self.id = id
+    
+    def regenerate(self):
+        """Replace the current session (with a new id)."""
+        if self.id is not None:
+            self.delete()
+        
+        old_session_was_locked = self.locked
+        if old_session_was_locked:
+            self.release_lock()
+        
+        self.id = None
         while self.id is None:
             self.id = self.generate_id()
             # Assert that the generated id is not already stored.
             if self._load() is not None:
                 self.id = None
+        
+        if old_session_was_locked:
+            self.acquire_lock()
     
     def clean_up(self):
         """Clean up expired sessions."""
     path_header: if 'path' is None (the default), then the response
         cookie 'path' will be pulled from request.headers[path_header].
     name: the name of the cookie.
-    timeout: the expiration timeout for the cookie.
+    timeout: the expiration timeout (in minutes) for both the cookie and
+        stored session data.
     domain: the cookie domain.
     secure: if False (the default) the cookie 'secure' value will not
         be set. If True, the cookie 'secure' value will be set (to 1).
     if not hasattr(cherrypy, "session"):
         cherrypy.session = cherrypy._ThreadLocalProxy('session')
     
+    set_response_cookie(path=path, path_header=path_header, name=name,
+                        timeout=timeout, domain=domain, secure=secure)
+
+
+def set_response_cookie(path=None, path_header=None, name='session_id',
+                        timeout=60, domain=None, secure=False):
+    """Set a response cookie for the client.
+    
+    path: the 'path' value to stick in the response cookie metadata.
+    path_header: if 'path' is None (the default), then the response
+        cookie 'path' will be pulled from request.headers[path_header].
+    name: the name of the cookie.
+    timeout: the expiration timeout for the cookie.
+    domain: the cookie domain.
+    secure: if False (the default) the cookie 'secure' value will not
+        be set. If True, the cookie 'secure' value will be set (to 1).
+    """
     # Set response cookie
     cookie = cherrypy.response.cookie
-    cookie[name] = sess.id
-    cookie[name]['path'] = path or request.headers.get(path_header) or '/'
+    cookie[name] = cherrypy.serving.session.id
+    cookie[name]['path'] = (path or cherrypy.request.headers.get(path_header)
+                            or '/')
     
     # We'd like to use the "max-age" param as indicated in
     # http://www.faqs.org/rfcs/rfc2109.html but IE doesn't
     t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", exp)
     cherrypy.response.cookie[name]['expires'] = t
 
+

cherrypy/test/test_session.py

         def restricted(self):
             return cherrypy.request.method
         restricted.exposed = True
+        
+        def regen(self):
+            cherrypy.tools.sessions.regenerate()
+            return "logged in"
+        regen.exposed = True
     
     cherrypy.tree.mount(Root())
     cherrypy.config.update({'environment': 'test_suite'})
         # code has to survive calling save/close without init.
         self.getPage('/restricted', self.cookies, method='POST')
         self.assertErrorPage(405, "Specified method is invalid for this server.")
+    
+    def test_6_regenerate(self):
+        self.getPage('/testStr')
+        # grab the cookie ID
+        id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
+        self.getPage('/regen')
+        self.assertBody('logged in')
+        id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
+        self.assertNotEqual(id1, id2)
 
 
 import socket
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.