bgneal@626: """Middleware for wiki integration. bgneal@626: bgneal@626: """ bgneal@626: import datetime bgneal@626: import hashlib bgneal@626: import logging bgneal@626: import random bgneal@626: import string bgneal@626: import time bgneal@626: bgneal@626: from django.conf import settings bgneal@626: import redis bgneal@626: bgneal@626: from core.services import get_redis_connection bgneal@626: bgneal@626: bgneal@626: SESSION_KEY = 'wiki_redis_key' bgneal@626: logger = logging.getLogger(__name__) bgneal@626: bgneal@626: bgneal@626: def cookie_value(user, now): bgneal@626: """Creates the value for the external wiki cookie.""" bgneal@626: bgneal@626: # The key part of the cookie is just a string that would make things bgneal@626: # difficult for a spoofer; something that can't be easily made up: bgneal@626: bgneal@626: h = hashlib.sha256() bgneal@626: h.update(user.username + user.email) bgneal@626: h.update(now.isoformat()) bgneal@626: h.update(''.join(random.sample(string.printable, 64))) bgneal@626: h.update(settings.SECRET_KEY) bgneal@626: key = h.hexdigest() bgneal@626: bgneal@626: parts = (user.username, user.email, key) bgneal@626: return '#'.join(parts) bgneal@626: bgneal@626: bgneal@626: def create_wiki_session(request, response): bgneal@626: """Sets up the session for the external wiki application. bgneal@626: bgneal@626: Creates the external cookie for the Wiki. bgneal@626: Updates the Redis set so the Wiki can verify the cookie. bgneal@626: bgneal@626: """ bgneal@626: now = datetime.datetime.utcnow() bgneal@626: value = cookie_value(request.user, now) bgneal@626: response.set_cookie(settings.WIKI_COOKIE_NAME, bgneal@626: value=value, bgneal@626: max_age=settings.WIKI_COOKIE_AGE, bgneal@626: domain=settings.WIKI_COOKIE_DOMAIN) bgneal@626: bgneal@626: # Update a sorted set in Redis with a hash of our cookie and a score bgneal@626: # of the current time as a timestamp. This allows us to delete old bgneal@626: # entries by score periodically. To verify the cookie, the external wiki bgneal@626: # application computes a hash of the cookie value and checks to see if bgneal@626: # it is in our Redis set. bgneal@626: bgneal@626: h = hashlib.sha256() bgneal@626: h.update(value) bgneal@626: name = h.hexdigest() bgneal@626: score = time.mktime(now.utctimetuple()) bgneal@626: conn = get_redis_connection() bgneal@626: bgneal@626: try: bgneal@626: conn.zadd(settings.WIKI_REDIS_SET, score, name) bgneal@626: except redis.RedisError: bgneal@626: logger.error("Error adding wiki cookie key") bgneal@626: bgneal@626: # Store the set member name in the session so we can delete it when the bgneal@626: # user logs out: bgneal@626: request.session[SESSION_KEY] = name bgneal@626: bgneal@626: bgneal@626: def destroy_wiki_session(request, response): bgneal@626: """Destroys the session for the external wiki application. bgneal@626: bgneal@626: Delete the external cookie. bgneal@626: Deletes the key from the Redis set as this entry is no longer valid. bgneal@626: bgneal@626: """ bgneal@626: response.delete_cookie(settings.WIKI_COOKIE_NAME, bgneal@626: domain=settings.WIKI_COOKIE_DOMAIN) bgneal@626: bgneal@626: try: bgneal@626: key = request.session[SESSION_KEY] bgneal@626: except KeyError: bgneal@626: # Hmmm, perhaps user logged in before this application was installed. bgneal@626: return bgneal@626: bgneal@626: conn = get_redis_connection() bgneal@626: try: bgneal@626: conn.zrem(settings.WIKI_REDIS_SET, key) bgneal@626: except redis.RedisError: bgneal@626: logger.error("Error deleting wiki cookie key") bgneal@626: bgneal@626: del request.session[SESSION_KEY] bgneal@626: bgneal@626: bgneal@626: class WikiMiddleware(object): bgneal@626: """ bgneal@626: Check for flags set in the session to determine when to set or delete an bgneal@626: external cookie for the wiki application. When creating a cookie, also bgneal@626: set an entry in Redis that the wiki application can validate to prevent bgneal@626: spoofing. bgneal@626: bgneal@626: """ bgneal@626: bgneal@626: def process_response(self, request, response): bgneal@626: bgneal@626: if request.session.get('wiki_set_cookie', False): bgneal@626: del request.session['wiki_set_cookie'] bgneal@626: bgneal@626: create_wiki_session(request, response) bgneal@626: bgneal@626: elif request.session.get('wiki_delete_cookie', False): bgneal@626: del request.session['wiki_delete_cookie'] bgneal@626: bgneal@626: destroy_wiki_session(request, response) bgneal@626: bgneal@626: return response