mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #19200 -- Session expiry with cached_db
Also did a little bit of cleanup.
This commit is contained in:
		| @@ -170,9 +170,13 @@ class SessionBase(object): | ||||
|  | ||||
|     _session = property(_get_session) | ||||
|  | ||||
|     def get_expiry_age(self): | ||||
|         """Get the number of seconds until the session expires.""" | ||||
|         expiry = self.get('_session_expiry') | ||||
|     def get_expiry_age(self, expiry=None): | ||||
|         """Get the number of seconds until the session expires. | ||||
|  | ||||
|         expiry is an optional parameter specifying the datetime of expiry. | ||||
|         """ | ||||
|         if expiry is None: | ||||
|             expiry = self.get('_session_expiry') | ||||
|         if not expiry:   # Checks both None and 0 cases | ||||
|             return settings.SESSION_COOKIE_AGE | ||||
|         if not isinstance(expiry, datetime): | ||||
|   | ||||
| @@ -2,9 +2,10 @@ | ||||
| Cached, database-backed sessions. | ||||
| """ | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.contrib.sessions.backends.db import SessionStore as DBStore | ||||
| from django.core.cache import cache | ||||
| from django.core.exceptions import SuspiciousOperation | ||||
| from django.utils import timezone | ||||
|  | ||||
| KEY_PREFIX = "django.contrib.sessions.cached_db" | ||||
|  | ||||
| @@ -28,9 +29,21 @@ class SessionStore(DBStore): | ||||
|             # Some backends (e.g. memcache) raise an exception on invalid | ||||
|             # cache keys. If this happens, reset the session. See #17810. | ||||
|             data = None | ||||
|  | ||||
|         if data is None: | ||||
|             data = super(SessionStore, self).load() | ||||
|             cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE) | ||||
|             # Duplicate DBStore.load, because we need to keep track | ||||
|             # of the expiry date to set it properly in the cache. | ||||
|             try: | ||||
|                 s = Session.objects.get( | ||||
|                     session_key=self.session_key, | ||||
|                     expire_date__gt=timezone.now() | ||||
|                 ) | ||||
|                 data = self.decode(s.session_data) | ||||
|                 cache.set(self.cache_key, data, | ||||
|                     self.get_expiry_age(s.expire_date)) | ||||
|             except (Session.DoesNotExist, SuspiciousOperation): | ||||
|                 self.create() | ||||
|                 data = {} | ||||
|         return data | ||||
|  | ||||
|     def exists(self, session_key): | ||||
| @@ -40,7 +53,7 @@ class SessionStore(DBStore): | ||||
|  | ||||
|     def save(self, must_create=False): | ||||
|         super(SessionStore, self).save(must_create) | ||||
|         cache.set(self.cache_key, self._session, settings.SESSION_COOKIE_AGE) | ||||
|         cache.set(self.cache_key, self._session, self.get_expiry_age()) | ||||
|  | ||||
|     def delete(self, session_key=None): | ||||
|         super(SessionStore, self).delete(session_key) | ||||
| @@ -58,3 +71,7 @@ class SessionStore(DBStore): | ||||
|         self.clear() | ||||
|         self.delete(self.session_key) | ||||
|         self.create() | ||||
|  | ||||
|  | ||||
| # At bottom to avoid circular import | ||||
| from django.contrib.sessions.models import Session | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class SessionStore(SessionBase): | ||||
|     def load(self): | ||||
|         try: | ||||
|             s = Session.objects.get( | ||||
|                 session_key = self.session_key, | ||||
|                 session_key=self.session_key, | ||||
|                 expire_date__gt=timezone.now() | ||||
|             ) | ||||
|             return self.decode(s.session_data) | ||||
|   | ||||
| @@ -32,6 +32,7 @@ class SessionStore(SessionBase): | ||||
|         try: | ||||
|             return signing.loads(self.session_key, | ||||
|                 serializer=PickleSerializer, | ||||
|                 # This doesn't handle non-default expiry dates, see #19201 | ||||
|                 max_age=settings.SESSION_COOKIE_AGE, | ||||
|                 salt='django.contrib.sessions.backends.signed_cookies') | ||||
|         except (signing.BadSignature, ValueError): | ||||
|   | ||||
| @@ -83,7 +83,7 @@ class SessionTestsMixin(object): | ||||
|         self.session['some key'] = 1 | ||||
|         self.session.modified = False | ||||
|         self.session.accessed = False | ||||
|         self.assertTrue('some key' in self.session) | ||||
|         self.assertIn('some key', self.session) | ||||
|         self.assertTrue(self.session.accessed) | ||||
|         self.assertFalse(self.session.modified) | ||||
|  | ||||
| @@ -200,28 +200,28 @@ class SessionTestsMixin(object): | ||||
|         # Using seconds | ||||
|         self.session.set_expiry(10) | ||||
|         delta = self.session.get_expiry_date() - timezone.now() | ||||
|         self.assertTrue(delta.seconds in (9, 10)) | ||||
|         self.assertIn(delta.seconds, (9, 10)) | ||||
|  | ||||
|         age = self.session.get_expiry_age() | ||||
|         self.assertTrue(age in (9, 10)) | ||||
|         self.assertIn(age, (9, 10)) | ||||
|  | ||||
|     def test_custom_expiry_timedelta(self): | ||||
|         # Using timedelta | ||||
|         self.session.set_expiry(timedelta(seconds=10)) | ||||
|         delta = self.session.get_expiry_date() - timezone.now() | ||||
|         self.assertTrue(delta.seconds in (9, 10)) | ||||
|         self.assertIn(delta.seconds, (9, 10)) | ||||
|  | ||||
|         age = self.session.get_expiry_age() | ||||
|         self.assertTrue(age in (9, 10)) | ||||
|         self.assertIn(age, (9, 10)) | ||||
|  | ||||
|     def test_custom_expiry_datetime(self): | ||||
|         # Using fixed datetime | ||||
|         self.session.set_expiry(timezone.now() + timedelta(seconds=10)) | ||||
|         delta = self.session.get_expiry_date() - timezone.now() | ||||
|         self.assertTrue(delta.seconds in (9, 10)) | ||||
|         self.assertIn(delta.seconds, (9, 10)) | ||||
|  | ||||
|         age = self.session.get_expiry_age() | ||||
|         self.assertTrue(age in (9, 10)) | ||||
|         self.assertIn(age, (9, 10)) | ||||
|  | ||||
|     def test_custom_expiry_reset(self): | ||||
|         self.session.set_expiry(None) | ||||
| @@ -258,6 +258,21 @@ class SessionTestsMixin(object): | ||||
|         encoded = self.session.encode(data) | ||||
|         self.assertEqual(self.session.decode(encoded), data) | ||||
|  | ||||
|     def test_actual_expiry(self): | ||||
|         # Regression test for #19200 | ||||
|         old_session_key = None | ||||
|         new_session_key = None | ||||
|         try: | ||||
|             self.session['foo'] = 'bar' | ||||
|             self.session.set_expiry(-timedelta(seconds=10)) | ||||
|             self.session.create() | ||||
|             # With an expiry date in the past, the session expires instantly. | ||||
|             new_session = self.backend(self.session.session_key) | ||||
|             self.assertNotIn('foo', new_session) | ||||
|         finally: | ||||
|             self.session.delete(old_session_key) | ||||
|             self.session.delete(new_session_key) | ||||
|  | ||||
|  | ||||
| class DatabaseSessionTests(SessionTestsMixin, TestCase): | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user