|
1 import base64 |
|
2 import md5 |
|
3 import os |
|
4 import random |
|
5 import sys |
|
6 import time |
|
7 from django.conf import settings |
|
8 from django.core.exceptions import SuspiciousOperation |
|
9 |
|
10 try: |
|
11 import cPickle as pickle |
|
12 except ImportError: |
|
13 import pickle |
|
14 |
|
15 class SessionBase(object): |
|
16 """ |
|
17 Base class for all Session classes. |
|
18 """ |
|
19 TEST_COOKIE_NAME = 'testcookie' |
|
20 TEST_COOKIE_VALUE = 'worked' |
|
21 |
|
22 def __init__(self, session_key=None): |
|
23 self._session_key = session_key |
|
24 self.accessed = False |
|
25 self.modified = False |
|
26 |
|
27 def __contains__(self, key): |
|
28 return key in self._session |
|
29 |
|
30 def __getitem__(self, key): |
|
31 return self._session[key] |
|
32 |
|
33 def __setitem__(self, key, value): |
|
34 self._session[key] = value |
|
35 self.modified = True |
|
36 |
|
37 def __delitem__(self, key): |
|
38 del self._session[key] |
|
39 self.modified = True |
|
40 |
|
41 def keys(self): |
|
42 return self._session.keys() |
|
43 |
|
44 def items(self): |
|
45 return self._session.items() |
|
46 |
|
47 def get(self, key, default=None): |
|
48 return self._session.get(key, default) |
|
49 |
|
50 def pop(self, key, *args): |
|
51 self.modified = self.modified or key in self._session |
|
52 return self._session.pop(key, *args) |
|
53 |
|
54 def setdefault(self, key, value): |
|
55 if key in self._session: |
|
56 return self._session[key] |
|
57 else: |
|
58 self.modified = True |
|
59 self._session[key] = value |
|
60 return value |
|
61 |
|
62 def set_test_cookie(self): |
|
63 self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE |
|
64 |
|
65 def test_cookie_worked(self): |
|
66 return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE |
|
67 |
|
68 def delete_test_cookie(self): |
|
69 del self[self.TEST_COOKIE_NAME] |
|
70 |
|
71 def encode(self, session_dict): |
|
72 "Returns the given session dictionary pickled and encoded as a string." |
|
73 pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) |
|
74 pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() |
|
75 return base64.encodestring(pickled + pickled_md5) |
|
76 |
|
77 def decode(self, session_data): |
|
78 encoded_data = base64.decodestring(session_data) |
|
79 pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] |
|
80 if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: |
|
81 raise SuspiciousOperation("User tampered with session cookie.") |
|
82 try: |
|
83 return pickle.loads(pickled) |
|
84 # Unpickling can cause a variety of exceptions. If something happens, |
|
85 # just return an empty dictionary (an empty session). |
|
86 except: |
|
87 return {} |
|
88 |
|
89 def _get_new_session_key(self): |
|
90 "Returns session key that isn't being used." |
|
91 # The random module is seeded when this Apache child is created. |
|
92 # Use settings.SECRET_KEY as added salt. |
|
93 try: |
|
94 pid = os.getpid() |
|
95 except AttributeError: |
|
96 # No getpid() in Jython, for example |
|
97 pid = 1 |
|
98 while 1: |
|
99 session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), |
|
100 pid, time.time(), settings.SECRET_KEY)).hexdigest() |
|
101 if not self.exists(session_key): |
|
102 break |
|
103 return session_key |
|
104 |
|
105 def _get_session_key(self): |
|
106 if self._session_key: |
|
107 return self._session_key |
|
108 else: |
|
109 self._session_key = self._get_new_session_key() |
|
110 return self._session_key |
|
111 |
|
112 def _set_session_key(self, session_key): |
|
113 self._session_key = session_key |
|
114 |
|
115 session_key = property(_get_session_key, _set_session_key) |
|
116 |
|
117 def _get_session(self): |
|
118 # Lazily loads session from storage. |
|
119 self.accessed = True |
|
120 try: |
|
121 return self._session_cache |
|
122 except AttributeError: |
|
123 if self._session_key is None: |
|
124 self._session_cache = {} |
|
125 else: |
|
126 self._session_cache = self.load() |
|
127 return self._session_cache |
|
128 |
|
129 _session = property(_get_session) |
|
130 |
|
131 # Methods that child classes must implement. |
|
132 |
|
133 def exists(self, session_key): |
|
134 """ |
|
135 Returns True if the given session_key already exists. |
|
136 """ |
|
137 raise NotImplementedError |
|
138 |
|
139 def save(self): |
|
140 """ |
|
141 Saves the session data. |
|
142 """ |
|
143 raise NotImplementedError |
|
144 |
|
145 def delete(self, session_key): |
|
146 """ |
|
147 Clears out the session data under this key. |
|
148 """ |
|
149 raise NotImplementedError |
|
150 |
|
151 def load(self): |
|
152 """ |
|
153 Loads the session data and returns a dictionary. |
|
154 """ |
|
155 raise NotImplementedError |