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@633
|
94 Check for flags on the request object 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
|