Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.5.x] Fixed #19200 -- Session expiry with cached_db

Also did a little bit of cleanup.

Backport of 04b00b6 from master.
  • Loading branch information...
commit 0fe02feab43aa0f0d0608de85cff7183055e6dd7 1 parent b760503
Aymeric Augustin authored October 27, 2012
10  django/contrib/sessions/backends/base.py
@@ -170,9 +170,13 @@ def _get_session(self, no_load=False):
170 170
 
171 171
     _session = property(_get_session)
172 172
 
173  
-    def get_expiry_age(self):
174  
-        """Get the number of seconds until the session expires."""
175  
-        expiry = self.get('_session_expiry')
  173
+    def get_expiry_age(self, expiry=None):
  174
+        """Get the number of seconds until the session expires.
  175
+
  176
+        expiry is an optional parameter specifying the datetime of expiry.
  177
+        """
  178
+        if expiry is None:
  179
+            expiry = self.get('_session_expiry')
176 180
         if not expiry:   # Checks both None and 0 cases
177 181
             return settings.SESSION_COOKIE_AGE
178 182
         if not isinstance(expiry, datetime):
25  django/contrib/sessions/backends/cached_db.py
@@ -2,9 +2,10 @@
2 2
 Cached, database-backed sessions.
3 3
 """
4 4
 
5  
-from django.conf import settings
6 5
 from django.contrib.sessions.backends.db import SessionStore as DBStore
7 6
 from django.core.cache import cache
  7
+from django.core.exceptions import SuspiciousOperation
  8
+from django.utils import timezone
8 9
 
9 10
 KEY_PREFIX = "django.contrib.sessions.cached_db"
10 11
 
@@ -28,9 +29,21 @@ def load(self):
28 29
             # Some backends (e.g. memcache) raise an exception on invalid
29 30
             # cache keys. If this happens, reset the session. See #17810.
30 31
             data = None
  32
+
31 33
         if data is None:
32  
-            data = super(SessionStore, self).load()
33  
-            cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
  34
+            # Duplicate DBStore.load, because we need to keep track
  35
+            # of the expiry date to set it properly in the cache.
  36
+            try:
  37
+                s = Session.objects.get(
  38
+                    session_key=self.session_key,
  39
+                    expire_date__gt=timezone.now()
  40
+                )
  41
+                data = self.decode(s.session_data)
  42
+                cache.set(self.cache_key, data,
  43
+                    self.get_expiry_age(s.expire_date))
  44
+            except (Session.DoesNotExist, SuspiciousOperation):
  45
+                self.create()
  46
+                data = {}
34 47
         return data
35 48
 
36 49
     def exists(self, session_key):
@@ -40,7 +53,7 @@ def exists(self, session_key):
40 53
 
41 54
     def save(self, must_create=False):
42 55
         super(SessionStore, self).save(must_create)
43  
-        cache.set(self.cache_key, self._session, settings.SESSION_COOKIE_AGE)
  56
+        cache.set(self.cache_key, self._session, self.get_expiry_age())
44 57
 
45 58
     def delete(self, session_key=None):
46 59
         super(SessionStore, self).delete(session_key)
@@ -58,3 +71,7 @@ def flush(self):
58 71
         self.clear()
59 72
         self.delete(self.session_key)
60 73
         self.create()
  74
+
  75
+
  76
+# At bottom to avoid circular import
  77
+from django.contrib.sessions.models import Session
2  django/contrib/sessions/backends/db.py
@@ -14,7 +14,7 @@ def __init__(self, session_key=None):
14 14
     def load(self):
15 15
         try:
16 16
             s = Session.objects.get(
17  
-                session_key = self.session_key,
  17
+                session_key=self.session_key,
18 18
                 expire_date__gt=timezone.now()
19 19
             )
20 20
             return self.decode(s.session_data)
1  django/contrib/sessions/backends/signed_cookies.py
@@ -32,6 +32,7 @@ def load(self):
32 32
         try:
33 33
             return signing.loads(self.session_key,
34 34
                 serializer=PickleSerializer,
  35
+                # This doesn't handle non-default expiry dates, see #19201
35 36
                 max_age=settings.SESSION_COOKIE_AGE,
36 37
                 salt='django.contrib.sessions.backends.signed_cookies')
37 38
         except (signing.BadSignature, ValueError):
29  django/contrib/sessions/tests.py
@@ -83,7 +83,7 @@ def test_has_key(self):
83 83
         self.session['some key'] = 1
84 84
         self.session.modified = False
85 85
         self.session.accessed = False
86  
-        self.assertTrue('some key' in self.session)
  86
+        self.assertIn('some key', self.session)
87 87
         self.assertTrue(self.session.accessed)
88 88
         self.assertFalse(self.session.modified)
89 89
 
@@ -200,28 +200,28 @@ def test_custom_expiry_seconds(self):
200 200
         # Using seconds
201 201
         self.session.set_expiry(10)
202 202
         delta = self.session.get_expiry_date() - timezone.now()
203  
-        self.assertTrue(delta.seconds in (9, 10))
  203
+        self.assertIn(delta.seconds, (9, 10))
204 204
 
205 205
         age = self.session.get_expiry_age()
206  
-        self.assertTrue(age in (9, 10))
  206
+        self.assertIn(age, (9, 10))
207 207
 
208 208
     def test_custom_expiry_timedelta(self):
209 209
         # Using timedelta
210 210
         self.session.set_expiry(timedelta(seconds=10))
211 211
         delta = self.session.get_expiry_date() - timezone.now()
212  
-        self.assertTrue(delta.seconds in (9, 10))
  212
+        self.assertIn(delta.seconds, (9, 10))
213 213
 
214 214
         age = self.session.get_expiry_age()
215  
-        self.assertTrue(age in (9, 10))
  215
+        self.assertIn(age, (9, 10))
216 216
 
217 217
     def test_custom_expiry_datetime(self):
218 218
         # Using fixed datetime
219 219
         self.session.set_expiry(timezone.now() + timedelta(seconds=10))
220 220
         delta = self.session.get_expiry_date() - timezone.now()
221  
-        self.assertTrue(delta.seconds in (9, 10))
  221
+        self.assertIn(delta.seconds, (9, 10))
222 222
 
223 223
         age = self.session.get_expiry_age()
224  
-        self.assertTrue(age in (9, 10))
  224
+        self.assertIn(age, (9, 10))
225 225
 
226 226
     def test_custom_expiry_reset(self):
227 227
         self.session.set_expiry(None)
@@ -258,6 +258,21 @@ def test_decode(self):
258 258
         encoded = self.session.encode(data)
259 259
         self.assertEqual(self.session.decode(encoded), data)
260 260
 
  261
+    def test_actual_expiry(self):
  262
+        # Regression test for #19200
  263
+        old_session_key = None
  264
+        new_session_key = None
  265
+        try:
  266
+            self.session['foo'] = 'bar'
  267
+            self.session.set_expiry(-timedelta(seconds=10))
  268
+            self.session.create()
  269
+            # With an expiry date in the past, the session expires instantly.
  270
+            new_session = self.backend(self.session.session_key)
  271
+            self.assertNotIn('foo', new_session)
  272
+        finally:
  273
+            self.session.delete(old_session_key)
  274
+            self.session.delete(new_session_key)
  275
+
261 276
 
262 277
 class DatabaseSessionTests(SessionTestsMixin, TestCase):
263 278
 

0 notes on commit 0fe02fe

Please sign in to comment.
Something went wrong with that request. Please try again.