annotate wiki/middleware.py @ 629:f4c043cf55ac

Wiki integration. Requests don't always have sessions. In particular this occurs when a request is made without a trailing slash. The Common middleware redirects when this happens, and the middleware process_request() processing stops before a session can get added. So just set an attribute on the request object for each operation. This seemed weird to me at first, but there are plenty of examples of this in the Django code base already.
author Brian Neal <bgneal@gmail.com>
date Tue, 13 Nov 2012 13:50:06 -0600
parents a4300639c6e7
children efac466a05d4
rev   line source
bgneal@626 1 """Middleware for wiki integration.
bgneal@626 2
bgneal@626 3 """
bgneal@626 4 import datetime
bgneal@626 5 import hashlib
bgneal@626 6 import logging
bgneal@626 7 import random
bgneal@626 8 import string
bgneal@626 9 import time
bgneal@626 10
bgneal@626 11 from django.conf import settings
bgneal@626 12 import redis
bgneal@626 13
bgneal@626 14 from core.services import get_redis_connection
bgneal@629 15 from wiki.constants import SESSION_SET_MEMBER
bgneal@626 16
bgneal@626 17
bgneal@626 18 logger = logging.getLogger(__name__)
bgneal@626 19
bgneal@626 20
bgneal@626 21 def cookie_value(user, now):
bgneal@626 22 """Creates the value for the external wiki cookie."""
bgneal@626 23
bgneal@626 24 # The key part of the cookie is just a string that would make things
bgneal@626 25 # difficult for a spoofer; something that can't be easily made up:
bgneal@626 26
bgneal@626 27 h = hashlib.sha256()
bgneal@626 28 h.update(user.username + user.email)
bgneal@626 29 h.update(now.isoformat())
bgneal@626 30 h.update(''.join(random.sample(string.printable, 64)))
bgneal@626 31 h.update(settings.SECRET_KEY)
bgneal@626 32 key = h.hexdigest()
bgneal@626 33
bgneal@626 34 parts = (user.username, user.email, key)
bgneal@626 35 return '#'.join(parts)
bgneal@626 36
bgneal@626 37
bgneal@626 38 def create_wiki_session(request, response):
bgneal@626 39 """Sets up the session for the external wiki application.
bgneal@626 40
bgneal@626 41 Creates the external cookie for the Wiki.
bgneal@626 42 Updates the Redis set so the Wiki can verify the cookie.
bgneal@626 43
bgneal@626 44 """
bgneal@626 45 now = datetime.datetime.utcnow()
bgneal@626 46 value = cookie_value(request.user, now)
bgneal@626 47 response.set_cookie(settings.WIKI_COOKIE_NAME,
bgneal@626 48 value=value,
bgneal@626 49 max_age=settings.WIKI_COOKIE_AGE,
bgneal@626 50 domain=settings.WIKI_COOKIE_DOMAIN)
bgneal@626 51
bgneal@626 52 # Update a sorted set in Redis with a hash of our cookie and a score
bgneal@626 53 # of the current time as a timestamp. This allows us to delete old
bgneal@626 54 # entries by score periodically. To verify the cookie, the external wiki
bgneal@626 55 # application computes a hash of the cookie value and checks to see if
bgneal@626 56 # it is in our Redis set.
bgneal@626 57
bgneal@626 58 h = hashlib.sha256()
bgneal@626 59 h.update(value)
bgneal@626 60 name = h.hexdigest()
bgneal@626 61 score = time.mktime(now.utctimetuple())
bgneal@626 62 conn = get_redis_connection()
bgneal@626 63
bgneal@626 64 try:
bgneal@626 65 conn.zadd(settings.WIKI_REDIS_SET, score, name)
bgneal@626 66 except redis.RedisError:
bgneal@626 67 logger.error("Error adding wiki cookie key")
bgneal@626 68
bgneal@626 69 # Store the set member name in the session so we can delete it when the
bgneal@626 70 # user logs out:
bgneal@627 71 request.session[SESSION_SET_MEMBER] = name
bgneal@626 72
bgneal@626 73
bgneal@627 74 def destroy_wiki_session(set_member, response):
bgneal@626 75 """Destroys the session for the external wiki application.
bgneal@626 76
bgneal@626 77 Delete the external cookie.
bgneal@627 78 Deletes the member from the Redis set as this entry is no longer valid.
bgneal@626 79
bgneal@626 80 """
bgneal@626 81 response.delete_cookie(settings.WIKI_COOKIE_NAME,
bgneal@626 82 domain=settings.WIKI_COOKIE_DOMAIN)
bgneal@626 83
bgneal@627 84 if set_member:
bgneal@627 85 conn = get_redis_connection()
bgneal@627 86 try:
bgneal@627 87 conn.zrem(settings.WIKI_REDIS_SET, set_member)
bgneal@627 88 except redis.RedisError:
bgneal@627 89 logger.error("Error deleting wiki cookie set member")
bgneal@626 90
bgneal@626 91
bgneal@626 92 class WikiMiddleware(object):
bgneal@626 93 """
bgneal@626 94 Check for flags set in the session to determine when to set or delete an
bgneal@626 95 external cookie for the wiki application. When creating a cookie, also
bgneal@626 96 set an entry in Redis that the wiki application can validate to prevent
bgneal@626 97 spoofing.
bgneal@626 98
bgneal@626 99 """
bgneal@626 100
bgneal@626 101 def process_response(self, request, response):
bgneal@626 102
bgneal@629 103 if hasattr(request, 'wiki_set_cookie'):
bgneal@626 104 create_wiki_session(request, response)
bgneal@627 105 elif hasattr(request, 'wiki_delete_cookie'):
bgneal@627 106 destroy_wiki_session(request.wiki_delete_cookie, response)
bgneal@626 107
bgneal@626 108 return response