# HG changeset patch # User Brian Neal # Date 1352754652 21600 # Node ID a4300639c6e7c75f0b818a1223bf1cc85c720350 # Parent a6bc1e2efa63c6e937c91dbfef2152c9e6a9458d Wiki integration. Create task to delete old cookie records. Rework logic upon logout, as session will not be available. Set an attribute on the request instead. diff -r a6bc1e2efa63 -r a4300639c6e7 sg101/settings/base.py --- a/sg101/settings/base.py Wed Nov 07 20:17:33 2012 -0600 +++ b/sg101/settings/base.py Mon Nov 12 15:10:52 2012 -0600 @@ -244,6 +244,10 @@ "task": "custom_search.tasks.process_search_queue_task", "schedule": crontab(minute='*/20'), }, + "expire_wiki_cookies": { + "task": "wiki.tasks.expire_cookies", + "schedule": crontab(minute=0, hour=2), + }, } ####################################################################### diff -r a6bc1e2efa63 -r a4300639c6e7 wiki/constants.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wiki/constants.py Mon Nov 12 15:10:52 2012 -0600 @@ -0,0 +1,6 @@ +"""Constants used throughout the wiki application + +""" + +SESSION_SET_FLAG = 'wiki_set_cookie' +SESSION_SET_MEMBER = 'wiki_redis_set_member' diff -r a6bc1e2efa63 -r a4300639c6e7 wiki/middleware.py --- a/wiki/middleware.py Wed Nov 07 20:17:33 2012 -0600 +++ b/wiki/middleware.py Mon Nov 12 15:10:52 2012 -0600 @@ -12,9 +12,9 @@ import redis from core.services import get_redis_connection +from wiki.constants import SESSION_SET_FLAG, SESSION_SET_MEMBER -SESSION_KEY = 'wiki_redis_key' logger = logging.getLogger(__name__) @@ -68,32 +68,25 @@ # Store the set member name in the session so we can delete it when the # user logs out: - request.session[SESSION_KEY] = name + request.session[SESSION_SET_MEMBER] = name -def destroy_wiki_session(request, response): +def destroy_wiki_session(set_member, response): """Destroys the session for the external wiki application. Delete the external cookie. - Deletes the key from the Redis set as this entry is no longer valid. + Deletes the member from the Redis set as this entry is no longer valid. """ response.delete_cookie(settings.WIKI_COOKIE_NAME, domain=settings.WIKI_COOKIE_DOMAIN) - try: - key = request.session[SESSION_KEY] - except KeyError: - # Hmmm, perhaps user logged in before this application was installed. - return - - conn = get_redis_connection() - try: - conn.zrem(settings.WIKI_REDIS_SET, key) - except redis.RedisError: - logger.error("Error deleting wiki cookie key") - - del request.session[SESSION_KEY] + if set_member: + conn = get_redis_connection() + try: + conn.zrem(settings.WIKI_REDIS_SET, set_member) + except redis.RedisError: + logger.error("Error deleting wiki cookie set member") class WikiMiddleware(object): @@ -107,14 +100,13 @@ def process_response(self, request, response): - if request.session.get('wiki_set_cookie', False): - del request.session['wiki_set_cookie'] + if request.session.get(SESSION_SET_FLAG, False): + del request.session[SESSION_SET_FLAG] create_wiki_session(request, response) - elif request.session.get('wiki_delete_cookie', False): - del request.session['wiki_delete_cookie'] + elif hasattr(request, 'wiki_delete_cookie'): - destroy_wiki_session(request, response) + destroy_wiki_session(request.wiki_delete_cookie, response) return response diff -r a6bc1e2efa63 -r a4300639c6e7 wiki/signals.py --- a/wiki/signals.py Wed Nov 07 20:17:33 2012 -0600 +++ b/wiki/signals.py Mon Nov 12 15:10:52 2012 -0600 @@ -5,6 +5,7 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out +from wiki.constants import SESSION_SET_FLAG, SESSION_SET_MEMBER logger = logging.getLogger(__name__) @@ -17,7 +18,7 @@ """ logger.info('User login: %s', user.username) - request.session['wiki_set_cookie'] = True + request.session[SESSION_SET_FLAG] = True def logout_callback(sender, request, user, **kwargs): @@ -25,10 +26,16 @@ Sets a flag for the middleware to delete the external cookie. + Since the user is about to logout, her session will be wiped out after + this function returns. This forces us to set an attribute on the request + object so that the response middleware can delete the wiki's cookie. + """ logger.info('User logout: %s', user.username) - request.session['wiki_delete_cookie'] = True + # Remember what Redis set member to delete by adding an attribute to the + # request object: + request.wiki_delete_cookie = request.session.get(SESSION_SET_MEMBER) user_logged_in.connect(login_callback, dispatch_uid='wiki.signals.login') diff -r a6bc1e2efa63 -r a4300639c6e7 wiki/tasks.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wiki/tasks.py Mon Nov 12 15:10:52 2012 -0600 @@ -0,0 +1,48 @@ +""" +Celery tasks for the wiki app. + +""" +import datetime +import logging +import time + +from celery.task import task +from django.conf import settings +import redis + +from core.services import get_redis_connection + + +logger = logging.getLogger(__name__) + + +@task +def expire_cookies(): + """ + Periodically run this task to remove expired cookies from the Redis set + that is shared between this Django application & the MoinMoin wiki for + authentication. + + """ + now = datetime.datetime.utcnow() + cutoff = now - datetime.timedelta(seconds=settings.WIKI_COOKIE_AGE) + min_score = time.mktime(cutoff.utctimetuple()) + + conn = get_redis_connection() + + set_name = settings.WIKI_REDIS_SET + try: + count = conn.zcard(set_name) + except redis.RedisError: + logger.error("Error getting zcard") + return + + try: + removed = conn.zremrangebyscore(set_name, 0.0, min_score) + except redis.RedisError: + logger.error("Error removing by score") + return + + total = count - removed + logger.info("Expire wiki cookies: removed %d, total is now %d", + removed, total) diff -r a6bc1e2efa63 -r a4300639c6e7 wiki/tests.py --- a/wiki/tests.py Wed Nov 07 20:17:33 2012 -0600 +++ b/wiki/tests.py Mon Nov 12 15:10:52 2012 -0600 @@ -12,7 +12,8 @@ from django.conf import settings from core.services import get_redis_connection -from wiki.middleware import WikiMiddleware, SESSION_KEY +from wiki.middleware import WikiMiddleware +from wiki.constants import SESSION_SET_FLAG, SESSION_SET_MEMBER class MiddleWareTestCase(TestCase): @@ -34,7 +35,7 @@ request.user = self.user response = HttpResponse() - request.session['wiki_set_cookie'] = True + request.session[SESSION_SET_FLAG] = True response = self.mw.process_response(request, response) self.assertIsNone(request.session.get('wiki_set_cookie')) @@ -50,7 +51,7 @@ cookie_val = cookie.value try: user, email, key = cookie_val.split('#') - except KeyError: + except ValueError: self.fail('invalid cookie value') else: self.assertEqual(user, self.user.username) @@ -68,16 +69,14 @@ session_start = datetime.datetime.fromtimestamp(score) self.assertLess(now - session_start, datetime.timedelta(seconds=2)) - session_key = request.session.get(SESSION_KEY) - self.assertTrue(session_key and session_key == member) + session_member = request.session.get(SESSION_SET_MEMBER) + self.assertTrue(session_member and session_member == member) # test the destroy session logic - request.session['wiki_delete_cookie'] = True + request.wiki_delete_cookie = member response = self.mw.process_response(request, response) - self.assertIsNone(request.session.get('wiki_delete_cookie')) - cookie = response.cookies.get(settings.WIKI_COOKIE_NAME) self.assertIsNotNone(cookie) if cookie: @@ -88,4 +87,3 @@ self.assertEqual(cookie['expires'], 'Thu, 01-Jan-1970 00:00:00 GMT') self.assertEqual(self.conn.zcard(settings.WIKI_REDIS_SET), 0) - self.assertIsNone(request.session.get(SESSION_KEY))