changeset 5425:bd55be7c3886

store expiry into sessions, moin maint cleansessions script, session enumeration support Note: at least the session enumeration support should be done in werkzeug, maybe even the handling of expiry info and purging expired sessions
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 12 Jan 2010 22:26:36 +0100
parents 7d0598a55c09
children 63df8c7b82b2
files MoinMoin/script/maint/cleansessions.py MoinMoin/web/session.py
diffstat 2 files changed, 96 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/script/maint/cleansessions.py	Tue Jan 12 22:26:36 2010 +0100
@@ -0,0 +1,74 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - cleansessions script
+
+@copyright: 2009 MoinMoin:ReimarBauer,
+            2010 MoinMoin:ThomasWaldmann
+@license: GNU GPL, see COPYING for details.
+"""
+
+import os, time
+
+from MoinMoin import user
+from MoinMoin.script import MoinScript
+
+class PluginScript(MoinScript):
+    """\
+Purpose:
+========
+This script allows you to clean up session files (usually used to maintain
+a "logged-in session" for http(s) or xmlrpc).
+
+Detailed Instructions:
+======================
+General syntax: moin [options] maint cleansessions [cleansessions-options]
+
+[options] usually should be:
+    --config-dir=/path/to/my/cfg/ --wiki-url=http://wiki.example.org/
+
+[cleansessions-options] see below:
+    --name     remove sessions only for user NAME (default: all users)
+    --all      remove all sessions (default: remove outdated sessions)
+"""
+
+    def __init__(self, argv, def_values):
+        MoinScript.__init__(self, argv, def_values)
+
+        self.parser.add_option(
+            "--name", metavar="NAME", dest="username",
+            help="remove sessions only for user NAME (default: all users)"
+        )
+        self.parser.add_option(
+            "--all", action="store_true", dest="all_sessions",
+            help="remove all sessions (default: remove outdated sessions)"
+        )
+
+    def mainloop(self):
+        self.init_request()
+        request = self.request
+        checks = []
+
+        if not self.options.all_sessions:
+            now = time.time()
+            checks.append(lambda session: session['expires'] < now)
+
+        if self.options.username:
+            u = user.User(request, None, self.options.username)
+            if not u.exists():
+                print 'User "%s" does not exist!' % self.options.username
+                return
+            else:
+                checks.append(lambda session: session['user.id'] == u.id)
+
+        session_service = request.cfg.session_service
+        for sid in session_service.get_all_session_ids(request):
+            session = session_service.get_session(request, sid)
+
+            # if ALL conditions are met, the session will be destroyed
+            killit = True
+            for check in checks:
+                killit = killit and check(session)
+
+            if killit:
+                session_service.destroy_session(request, session)
+
--- a/MoinMoin/web/session.py	Mon Jan 11 08:59:27 2010 +0100
+++ b/MoinMoin/web/session.py	Tue Jan 12 22:26:36 2010 +0100
@@ -11,7 +11,7 @@
                 2009 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
-import time
+import time, os
 
 from werkzeug.contrib.sessions import FilesystemSessionStore, Session
 
@@ -45,6 +45,15 @@
         """
         raise NotImplementedError
 
+    def get_all_session_ids(self, request):
+        """
+        Return a list of all session ids known to the SessionService.
+
+        TODO: make it a generator
+        """
+        raise NotImplementedError
+
+
 def _get_session_lifetime(request, userobj):
     """ Get session lifetime for the user object userobj
     Cookie lifetime in hours, can be fractional. First tuple element is for anonymous sessions,
@@ -137,6 +146,11 @@
             session = store.get(sid)
         return session
 
+    def get_all_session_ids(self, request):
+        # TODO: this should be done by werkzeug's FilesystemSessionStore
+        # the sids are the same as the filenames, see filename_template above
+        return os.listdir(request.cfg.session_dir)
+
     def destroy_session(self, request, session):
         session.clear()
         store = self._store_get(request)
@@ -185,7 +199,14 @@
                                path=cookie_path, domain=cfg.cookie_domain,
                                secure=cookie_secure, httponly=cfg.cookie_httponly)
 
+            # add some info about expiry to the sessions, so we can purge them:
+            session['expires'] = cookie_expires
+
             if session.should_save:
+                # note: currently, every request of a logged-in user will save
+                # the session, even when always requesting same page. As we
+                # store the page trail into the session, we would save rather
+                # often anyway, though.
                 store = self._store_get(request)
                 logging.debug("saving session: %r" % session)
                 store.save(session)