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@626
|
15
|
bgneal@626
|
16
|
bgneal@626
|
17 SESSION_KEY = 'wiki_redis_key'
|
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@626
|
71 request.session[SESSION_KEY] = name
|
bgneal@626
|
72
|
bgneal@626
|
73
|
bgneal@626
|
74 def destroy_wiki_session(request, response):
|
bgneal@626
|
75 """Destroys the session for the external wiki application.
|
bgneal@626
|
76
|
bgneal@626
|
77 Delete the external cookie.
|
bgneal@626
|
78 Deletes the key 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@626
|
84 try:
|
bgneal@626
|
85 key = request.session[SESSION_KEY]
|
bgneal@626
|
86 except KeyError:
|
bgneal@626
|
87 # Hmmm, perhaps user logged in before this application was installed.
|
bgneal@626
|
88 return
|
bgneal@626
|
89
|
bgneal@626
|
90 conn = get_redis_connection()
|
bgneal@626
|
91 try:
|
bgneal@626
|
92 conn.zrem(settings.WIKI_REDIS_SET, key)
|
bgneal@626
|
93 except redis.RedisError:
|
bgneal@626
|
94 logger.error("Error deleting wiki cookie key")
|
bgneal@626
|
95
|
bgneal@626
|
96 del request.session[SESSION_KEY]
|
bgneal@626
|
97
|
bgneal@626
|
98
|
bgneal@626
|
99 class WikiMiddleware(object):
|
bgneal@626
|
100 """
|
bgneal@626
|
101 Check for flags set in the session to determine when to set or delete an
|
bgneal@626
|
102 external cookie for the wiki application. When creating a cookie, also
|
bgneal@626
|
103 set an entry in Redis that the wiki application can validate to prevent
|
bgneal@626
|
104 spoofing.
|
bgneal@626
|
105
|
bgneal@626
|
106 """
|
bgneal@626
|
107
|
bgneal@626
|
108 def process_response(self, request, response):
|
bgneal@626
|
109
|
bgneal@626
|
110 if request.session.get('wiki_set_cookie', False):
|
bgneal@626
|
111 del request.session['wiki_set_cookie']
|
bgneal@626
|
112
|
bgneal@626
|
113 create_wiki_session(request, response)
|
bgneal@626
|
114
|
bgneal@626
|
115 elif request.session.get('wiki_delete_cookie', False):
|
bgneal@626
|
116 del request.session['wiki_delete_cookie']
|
bgneal@626
|
117
|
bgneal@626
|
118 destroy_wiki_session(request, response)
|
bgneal@626
|
119
|
bgneal@626
|
120 return response
|