annotate wiki/middleware.py @ 861:e4f8d87c3d30

Configure Markdown logger to reduce noise in logs. Markdown is logging at the INFO level whenever it loads an extension. This looks like it has been fixed in master at GitHub. But until then we will explicitly configure the MARKDOWN logger to log at WARNING or higher.
author Brian Neal <bgneal@gmail.com>
date Mon, 01 Dec 2014 18:36:27 -0600
parents 47521d9e94bd
children ba3230aba90c
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@841 28 h.update(user.username.encode('utf-8'))
bgneal@841 29 h.update(user.email.encode('utf-8'))
bgneal@626 30 h.update(now.isoformat())
bgneal@626 31 h.update(''.join(random.sample(string.printable, 64)))
bgneal@626 32 h.update(settings.SECRET_KEY)
bgneal@626 33 key = h.hexdigest()
bgneal@626 34
bgneal@626 35 parts = (user.username, user.email, key)
bgneal@626 36 return '#'.join(parts)
bgneal@626 37
bgneal@626 38
bgneal@626 39 def create_wiki_session(request, response):
bgneal@626 40 """Sets up the session for the external wiki application.
bgneal@626 41
bgneal@626 42 Creates the external cookie for the Wiki.
bgneal@626 43 Updates the Redis set so the Wiki can verify the cookie.
bgneal@626 44
bgneal@626 45 """
bgneal@626 46 now = datetime.datetime.utcnow()
bgneal@626 47 value = cookie_value(request.user, now)
bgneal@626 48 response.set_cookie(settings.WIKI_COOKIE_NAME,
bgneal@626 49 value=value,
bgneal@626 50 max_age=settings.WIKI_COOKIE_AGE,
bgneal@626 51 domain=settings.WIKI_COOKIE_DOMAIN)
bgneal@626 52
bgneal@626 53 # Update a sorted set in Redis with a hash of our cookie and a score
bgneal@626 54 # of the current time as a timestamp. This allows us to delete old
bgneal@626 55 # entries by score periodically. To verify the cookie, the external wiki
bgneal@626 56 # application computes a hash of the cookie value and checks to see if
bgneal@626 57 # it is in our Redis set.
bgneal@626 58
bgneal@626 59 h = hashlib.sha256()
bgneal@626 60 h.update(value)
bgneal@626 61 name = h.hexdigest()
bgneal@626 62 score = time.mktime(now.utctimetuple())
bgneal@626 63 conn = get_redis_connection()
bgneal@626 64
bgneal@626 65 try:
bgneal@626 66 conn.zadd(settings.WIKI_REDIS_SET, score, name)
bgneal@626 67 except redis.RedisError:
bgneal@626 68 logger.error("Error adding wiki cookie key")
bgneal@626 69
bgneal@626 70 # Store the set member name in the session so we can delete it when the
bgneal@626 71 # user logs out:
bgneal@627 72 request.session[SESSION_SET_MEMBER] = name
bgneal@626 73
bgneal@626 74
bgneal@627 75 def destroy_wiki_session(set_member, response):
bgneal@626 76 """Destroys the session for the external wiki application.
bgneal@626 77
bgneal@626 78 Delete the external cookie.
bgneal@627 79 Deletes the member from the Redis set as this entry is no longer valid.
bgneal@626 80
bgneal@626 81 """
bgneal@626 82 response.delete_cookie(settings.WIKI_COOKIE_NAME,
bgneal@626 83 domain=settings.WIKI_COOKIE_DOMAIN)
bgneal@626 84
bgneal@627 85 if set_member:
bgneal@627 86 conn = get_redis_connection()
bgneal@627 87 try:
bgneal@627 88 conn.zrem(settings.WIKI_REDIS_SET, set_member)
bgneal@627 89 except redis.RedisError:
bgneal@627 90 logger.error("Error deleting wiki cookie set member")
bgneal@626 91
bgneal@626 92
bgneal@626 93 class WikiMiddleware(object):
bgneal@626 94 """
bgneal@633 95 Check for flags on the request object to determine when to set or delete an
bgneal@626 96 external cookie for the wiki application. When creating a cookie, also
bgneal@626 97 set an entry in Redis that the wiki application can validate to prevent
bgneal@626 98 spoofing.
bgneal@626 99
bgneal@626 100 """
bgneal@626 101
bgneal@626 102 def process_response(self, request, response):
bgneal@626 103
bgneal@629 104 if hasattr(request, 'wiki_set_cookie'):
bgneal@626 105 create_wiki_session(request, response)
bgneal@627 106 elif hasattr(request, 'wiki_delete_cookie'):
bgneal@627 107 destroy_wiki_session(request.wiki_delete_cookie, response)
bgneal@626 108
bgneal@626 109 return response