mirror of https://github.com/django/django.git
Fixed #17810. Catch session key errors.
Catches memcached session key errors related to overly long session keys. This is a long-standing bug, but severity was exacerbated by the addition of cookie-backed session storage, which generates long session values. If an installation switched from cookie-backed session store to memcached, users would not be able to log in because of the server error from overly long memcached keys. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17795 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
38061221c3
commit
2ca9801956
|
@ -16,7 +16,13 @@ class SessionStore(SessionBase):
|
|||
return KEY_PREFIX + self._get_or_create_session_key()
|
||||
|
||||
def load(self):
|
||||
session_data = self._cache.get(self.cache_key)
|
||||
try:
|
||||
session_data = self._cache.get(self.cache_key, None)
|
||||
except Exception as e:
|
||||
e_type = str(type(e))
|
||||
if e_type != "<class 'memcache.MemcachedKeyLengthError'>":
|
||||
raise e
|
||||
session_data = None
|
||||
if session_data is not None:
|
||||
return session_data
|
||||
self.create()
|
||||
|
|
|
@ -21,7 +21,13 @@ class SessionStore(DBStore):
|
|||
return KEY_PREFIX + self._get_or_create_session_key()
|
||||
|
||||
def load(self):
|
||||
data = cache.get(self.cache_key, None)
|
||||
try:
|
||||
data = cache.get(self.cache_key, None)
|
||||
except Exception as e:
|
||||
e_type = str(type(e))
|
||||
if e_type != "<class 'memcache.MemcachedKeyLengthError'>":
|
||||
raise e
|
||||
data = None
|
||||
if data is None:
|
||||
data = super(SessionStore, self).load()
|
||||
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
|
||||
|
|
|
@ -2,7 +2,9 @@ from __future__ import with_statement
|
|||
|
||||
from datetime import datetime, timedelta
|
||||
import shutil
|
||||
import string
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
|
||||
|
@ -12,10 +14,11 @@ from django.contrib.sessions.backends.file import SessionStore as FileSession
|
|||
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.core.cache.backends.base import CacheKeyWarning
|
||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
||||
from django.http import HttpResponse
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.test.utils import override_settings, get_warnings_state, restore_warnings_state
|
||||
from django.utils import timezone
|
||||
from django.utils import unittest
|
||||
|
||||
|
@ -25,7 +28,7 @@ class SessionTestsMixin(object):
|
|||
# class, which wouldn't work, and to allow different TestCase subclasses to
|
||||
# be used.
|
||||
|
||||
backend = None # subclasses must specify
|
||||
backend = None # subclasses must specify
|
||||
|
||||
def setUp(self):
|
||||
self.session = self.backend()
|
||||
|
@ -119,13 +122,13 @@ class SessionTestsMixin(object):
|
|||
self.assertTrue(hasattr(i, '__iter__'))
|
||||
self.assertTrue(self.session.accessed)
|
||||
self.assertFalse(self.session.modified)
|
||||
self.assertEqual(list(i), [('x',1)])
|
||||
self.assertEqual(list(i), [('x', 1)])
|
||||
|
||||
def test_clear(self):
|
||||
self.session['x'] = 1
|
||||
self.session.modified = False
|
||||
self.session.accessed = False
|
||||
self.assertEqual(self.session.items(), [('x',1)])
|
||||
self.assertEqual(self.session.items(), [('x', 1)])
|
||||
self.session.clear()
|
||||
self.assertEqual(self.session.items(), [])
|
||||
self.assertTrue(self.session.accessed)
|
||||
|
@ -280,7 +283,7 @@ class DatabaseSessionTests(SessionTestsMixin, TestCase):
|
|||
|
||||
s = Session.objects.get(session_key=self.session.session_key)
|
||||
# Change it
|
||||
Session.objects.save(s.session_key, {'y':2}, s.expire_date)
|
||||
Session.objects.save(s.session_key, {'y': 2}, s.expire_date)
|
||||
# Clear cache, so that it will be retrieved from DB
|
||||
del self.session._session_cache
|
||||
self.assertEqual(self.session['y'], 2)
|
||||
|
@ -298,6 +301,14 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
|
|||
with self.assertNumQueries(0):
|
||||
self.assertTrue(self.session.exists(self.session.session_key))
|
||||
|
||||
def test_load_overlong_key(self):
|
||||
warnings_state = get_warnings_state()
|
||||
warnings.filterwarnings('ignore',
|
||||
category=CacheKeyWarning)
|
||||
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
||||
self.assertEqual(self.session.load(), {})
|
||||
restore_warnings_state(warnings_state)
|
||||
|
||||
|
||||
CacheDBSessionWithTimeZoneTests = override_settings(USE_TZ=True)(CacheDBSessionTests)
|
||||
|
||||
|
@ -339,6 +350,14 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
|||
|
||||
backend = CacheSession
|
||||
|
||||
def test_load_overlong_key(self):
|
||||
warnings_state = get_warnings_state()
|
||||
warnings.filterwarnings('ignore',
|
||||
category=CacheKeyWarning)
|
||||
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
||||
self.assertEqual(self.session.load(), {})
|
||||
restore_warnings_state(warnings_state)
|
||||
|
||||
|
||||
class SessionMiddlewareTests(unittest.TestCase):
|
||||
|
||||
|
@ -394,6 +413,7 @@ class SessionMiddlewareTests(unittest.TestCase):
|
|||
self.assertNotIn('httponly',
|
||||
str(response.cookies[settings.SESSION_COOKIE_NAME]))
|
||||
|
||||
|
||||
class CookieSessionTests(SessionTestsMixin, TestCase):
|
||||
|
||||
backend = CookieSession
|
||||
|
|
Loading…
Reference in New Issue