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
|