Fixed #31864 -- Fixed encoding session data during transition to Django 3.1.

Thanks אורי for the report.
This commit is contained in:
Mariusz Felisiak 2020-08-07 21:42:39 +02:00 committed by GitHub
parent e5118b545b
commit 99abfe8f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 6 deletions

View File

@ -108,6 +108,9 @@ class SessionBase:
def encode(self, session_dict): def encode(self, session_dict):
"Return the given session dictionary serialized and encoded as a string." "Return the given session dictionary serialized and encoded as a string."
# RemovedInDjango40Warning: DEFAULT_HASHING_ALGORITHM will be removed.
if settings.DEFAULT_HASHING_ALGORITHM == 'sha1':
return self._legacy_encode(session_dict)
return signing.dumps( return signing.dumps(
session_dict, salt=self.key_salt, serializer=self.serializer, session_dict, salt=self.key_salt, serializer=self.serializer,
compress=True, compress=True,
@ -121,6 +124,12 @@ class SessionBase:
except Exception: except Exception:
return self._legacy_decode(session_data) return self._legacy_decode(session_data)
def _legacy_encode(self, session_dict):
# RemovedInDjango40Warning.
serialized = self.serializer().dumps(session_dict)
hash = self._hash(serialized)
return base64.b64encode(hash.encode() + b':' + serialized).decode('ascii')
def _legacy_decode(self, session_data): def _legacy_decode(self, session_data):
# RemovedInDjango40Warning: pre-Django 3.1 format will be invalid. # RemovedInDjango40Warning: pre-Django 3.1 format will be invalid.
encoded_data = base64.b64decode(session_data.encode('ascii')) encoded_data = base64.b64decode(session_data.encode('ascii'))

View File

@ -14,3 +14,6 @@ Bugfixes
* Fixed wrapping of long model names in the admin's navigation sidebar * Fixed wrapping of long model names in the admin's navigation sidebar
(:ticket:`31854`). (:ticket:`31854`).
* Fixed encoding session data while upgrading multiple instances of the same
project to Django 3.1 (:ticket:`31864`).

View File

@ -31,9 +31,11 @@ from django.core.cache.backends.base import InvalidCacheBackendError
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.http import HttpResponse from django.http import HttpResponse
from django.test import ( from django.test import (
RequestFactory, TestCase, ignore_warnings, override_settings, RequestFactory, SimpleTestCase, TestCase, ignore_warnings,
override_settings,
) )
from django.utils import timezone from django.utils import timezone
from django.utils.deprecation import RemovedInDjango40Warning
from .models import SessionStore as CustomDatabaseSession from .models import SessionStore as CustomDatabaseSession
@ -323,6 +325,13 @@ class SessionTestsMixin:
{'a test key': 'a test value'}, {'a test key': 'a test value'},
) )
@ignore_warnings(category=RemovedInDjango40Warning)
def test_default_hashing_algorith_legacy_decode(self):
with self.settings(DEFAULT_HASHING_ALGORITHM='sha1'):
data = {'a test key': 'a test value'}
encoded = self.session.encode(data)
self.assertEqual(self.session._legacy_decode(encoded), data)
def test_decode_failure_logged_to_security(self): def test_decode_failure_logged_to_security(self):
bad_encode = base64.b64encode(b'flaskdj:alkdjf').decode('ascii') bad_encode = base64.b64encode(b'flaskdj:alkdjf').decode('ascii')
with self.assertLogs('django.security.SuspiciousSession', 'WARNING') as cm: with self.assertLogs('django.security.SuspiciousSession', 'WARNING') as cm:
@ -526,8 +535,7 @@ class CacheDBSessionWithTimeZoneTests(CacheDBSessionTests):
pass pass
# Don't need DB flushing for these tests, so can use unittest.TestCase as base class class FileSessionTests(SessionTestsMixin, SimpleTestCase):
class FileSessionTests(SessionTestsMixin, unittest.TestCase):
backend = FileSession backend = FileSession
@ -620,7 +628,7 @@ class FileSessionPathLibTests(FileSessionTests):
return Path(tmp_dir) return Path(tmp_dir)
class CacheSessionTests(SessionTestsMixin, unittest.TestCase): class CacheSessionTests(SessionTestsMixin, SimpleTestCase):
backend = CacheSession backend = CacheSession
@ -854,8 +862,7 @@ class SessionMiddlewareTests(TestCase):
self.assertEqual(response['Vary'], 'Cookie') self.assertEqual(response['Vary'], 'Cookie')
# Don't need DB flushing for these tests, so can use unittest.TestCase as base class class CookieSessionTests(SessionTestsMixin, SimpleTestCase):
class CookieSessionTests(SessionTestsMixin, unittest.TestCase):
backend = CookieSession backend = CookieSession