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