测试gitnore
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
import base64
|
||||
import logging
|
||||
import string
|
||||
import warnings
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.exceptions import SuspiciousSession
|
||||
from django.core import signing
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.crypto import (
|
||||
constant_time_compare, get_random_string, salted_hmac,
|
||||
)
|
||||
from django.utils.deprecation import RemovedInDjango40Warning
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
|
||||
# session_key should not be case sensitive because some backends can store it
|
||||
# on case insensitive file systems.
|
||||
@@ -18,7 +26,6 @@ class CreateError(Exception):
|
||||
Used internally as a consistent exception type to catch from save (see the
|
||||
docstring for SessionBase.save() for details).
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -26,7 +33,6 @@ class UpdateError(Exception):
|
||||
"""
|
||||
Occurs if Django tries to update a session that was deleted.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -34,9 +40,8 @@ class SessionBase:
|
||||
"""
|
||||
Base class for all Session classes.
|
||||
"""
|
||||
|
||||
TEST_COOKIE_NAME = "testcookie"
|
||||
TEST_COOKIE_VALUE = "worked"
|
||||
TEST_COOKIE_NAME = 'testcookie'
|
||||
TEST_COOKIE_VALUE = 'worked'
|
||||
|
||||
__not_given = object()
|
||||
|
||||
@@ -50,6 +55,13 @@ class SessionBase:
|
||||
return key in self._session
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == LANGUAGE_SESSION_KEY:
|
||||
warnings.warn(
|
||||
'The user language will no longer be stored in '
|
||||
'request.session in Django 4.0. Read it from '
|
||||
'request.COOKIES[settings.LANGUAGE_COOKIE_NAME] instead.',
|
||||
RemovedInDjango40Warning, stacklevel=2,
|
||||
)
|
||||
return self._session[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
@@ -62,7 +74,7 @@ class SessionBase:
|
||||
|
||||
@property
|
||||
def key_salt(self):
|
||||
return "django.contrib.sessions." + self.__class__.__qualname__
|
||||
return 'django.contrib.sessions.' + self.__class__.__qualname__
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._session.get(key, default)
|
||||
@@ -89,28 +101,62 @@ class SessionBase:
|
||||
def delete_test_cookie(self):
|
||||
del self[self.TEST_COOKIE_NAME]
|
||||
|
||||
def _hash(self, value):
|
||||
# RemovedInDjango40Warning: pre-Django 3.1 format will be invalid.
|
||||
key_salt = "django.contrib.sessions" + self.__class__.__name__
|
||||
return salted_hmac(key_salt, value).hexdigest()
|
||||
|
||||
def encode(self, session_dict):
|
||||
"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(
|
||||
session_dict,
|
||||
salt=self.key_salt,
|
||||
serializer=self.serializer,
|
||||
session_dict, salt=self.key_salt, serializer=self.serializer,
|
||||
compress=True,
|
||||
)
|
||||
|
||||
def decode(self, session_data):
|
||||
try:
|
||||
return signing.loads(
|
||||
session_data, salt=self.key_salt, serializer=self.serializer
|
||||
)
|
||||
return signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)
|
||||
# RemovedInDjango40Warning: when the deprecation ends, handle here
|
||||
# exceptions similar to what _legacy_decode() does now.
|
||||
except signing.BadSignature:
|
||||
logger = logging.getLogger("django.security.SuspiciousSession")
|
||||
logger.warning("Session data corrupted")
|
||||
try:
|
||||
# Return an empty session if data is not in the pre-Django 3.1
|
||||
# format.
|
||||
return self._legacy_decode(session_data)
|
||||
except Exception:
|
||||
logger = logging.getLogger('django.security.SuspiciousSession')
|
||||
logger.warning('Session data corrupted')
|
||||
return {}
|
||||
except Exception:
|
||||
# ValueError, unpickling exceptions. If any of these happen, just
|
||||
# return an empty dictionary (an empty session).
|
||||
pass
|
||||
return {}
|
||||
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):
|
||||
# RemovedInDjango40Warning: pre-Django 3.1 format will be invalid.
|
||||
encoded_data = base64.b64decode(session_data.encode('ascii'))
|
||||
try:
|
||||
# could produce ValueError if there is no ':'
|
||||
hash, serialized = encoded_data.split(b':', 1)
|
||||
expected_hash = self._hash(serialized)
|
||||
if not constant_time_compare(hash.decode(), expected_hash):
|
||||
raise SuspiciousSession("Session data corrupted")
|
||||
else:
|
||||
return self.serializer().loads(serialized)
|
||||
except Exception as e:
|
||||
# ValueError, SuspiciousOperation, unpickling exceptions. If any of
|
||||
# these happen, just return an empty dictionary (an empty session).
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
return {}
|
||||
|
||||
def update(self, dict_):
|
||||
self._session.update(dict_)
|
||||
@@ -204,18 +250,18 @@ class SessionBase:
|
||||
arguments specifying the modification and expiry of the session.
|
||||
"""
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
modification = kwargs['modification']
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
# Make the difference between "expiry=None passed in kwargs" and
|
||||
# "expiry not passed in kwargs", in order to guarantee not to trigger
|
||||
# self.load() when expiry is provided.
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
expiry = kwargs['expiry']
|
||||
except KeyError:
|
||||
expiry = self.get("_session_expiry")
|
||||
expiry = self.get('_session_expiry')
|
||||
|
||||
if not expiry: # Checks both None and 0 cases
|
||||
if not expiry: # Checks both None and 0 cases
|
||||
return self.get_session_cookie_age()
|
||||
if not isinstance(expiry, datetime):
|
||||
return expiry
|
||||
@@ -229,14 +275,14 @@ class SessionBase:
|
||||
arguments specifying the modification and expiry of the session.
|
||||
"""
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
modification = kwargs['modification']
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
# Same comment as in get_expiry_age
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
expiry = kwargs['expiry']
|
||||
except KeyError:
|
||||
expiry = self.get("_session_expiry")
|
||||
expiry = self.get('_session_expiry')
|
||||
|
||||
if isinstance(expiry, datetime):
|
||||
return expiry
|
||||
@@ -261,13 +307,13 @@ class SessionBase:
|
||||
if value is None:
|
||||
# Remove any custom expiration for this session.
|
||||
try:
|
||||
del self["_session_expiry"]
|
||||
del self['_session_expiry']
|
||||
except KeyError:
|
||||
pass
|
||||
return
|
||||
if isinstance(value, timedelta):
|
||||
value = timezone.now() + value
|
||||
self["_session_expiry"] = value
|
||||
self['_session_expiry'] = value
|
||||
|
||||
def get_expire_at_browser_close(self):
|
||||
"""
|
||||
@@ -276,9 +322,9 @@ class SessionBase:
|
||||
``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
|
||||
date/age, if there is one.
|
||||
"""
|
||||
if self.get("_session_expiry") is None:
|
||||
if self.get('_session_expiry') is None:
|
||||
return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
|
||||
return self.get("_session_expiry") == 0
|
||||
return self.get('_session_expiry') == 0
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
@@ -306,9 +352,7 @@ class SessionBase:
|
||||
"""
|
||||
Return True if the given session_key already exists.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide an exists() method"
|
||||
)
|
||||
raise NotImplementedError('subclasses of SessionBase must provide an exists() method')
|
||||
|
||||
def create(self):
|
||||
"""
|
||||
@@ -316,9 +360,7 @@ class SessionBase:
|
||||
a unique key and will have saved the result once (with empty data)
|
||||
before the method returns.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a create() method"
|
||||
)
|
||||
raise NotImplementedError('subclasses of SessionBase must provide a create() method')
|
||||
|
||||
def save(self, must_create=False):
|
||||
"""
|
||||
@@ -326,26 +368,20 @@ class SessionBase:
|
||||
object (or raise CreateError). Otherwise, only update an existing
|
||||
object and don't create one (raise UpdateError if needed).
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a save() method"
|
||||
)
|
||||
raise NotImplementedError('subclasses of SessionBase must provide a save() method')
|
||||
|
||||
def delete(self, session_key=None):
|
||||
"""
|
||||
Delete the session data under this key. If the key is None, use the
|
||||
current session key value.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a delete() method"
|
||||
)
|
||||
raise NotImplementedError('subclasses of SessionBase must provide a delete() method')
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the session data and return a dictionary.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a load() method"
|
||||
)
|
||||
raise NotImplementedError('subclasses of SessionBase must provide a load() method')
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
@@ -356,4 +392,4 @@ class SessionBase:
|
||||
NotImplementedError. If it isn't necessary, because the backend has
|
||||
a built-in expiration mechanism, it should be a no-op.
|
||||
"""
|
||||
raise NotImplementedError("This backend does not support clear_expired().")
|
||||
raise NotImplementedError('This backend does not support clear_expired().')
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
|
||||
from django.contrib.sessions.backends.base import (
|
||||
CreateError, SessionBase, UpdateError,
|
||||
)
|
||||
from django.core.cache import caches
|
||||
|
||||
KEY_PREFIX = "django.contrib.sessions.cache"
|
||||
@@ -9,7 +11,6 @@ class SessionStore(SessionBase):
|
||||
"""
|
||||
A cache-based session store.
|
||||
"""
|
||||
|
||||
cache_key_prefix = KEY_PREFIX
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
@@ -48,8 +49,7 @@ class SessionStore(SessionBase):
|
||||
return
|
||||
raise RuntimeError(
|
||||
"Unable to create a new session key. "
|
||||
"It is likely that the cache is unavailable."
|
||||
)
|
||||
"It is likely that the cache is unavailable.")
|
||||
|
||||
def save(self, must_create=False):
|
||||
if self.session_key is None:
|
||||
@@ -60,18 +60,14 @@ class SessionStore(SessionBase):
|
||||
func = self._cache.set
|
||||
else:
|
||||
raise UpdateError
|
||||
result = func(
|
||||
self.cache_key,
|
||||
self._get_session(no_load=must_create),
|
||||
self.get_expiry_age(),
|
||||
)
|
||||
result = func(self.cache_key,
|
||||
self._get_session(no_load=must_create),
|
||||
self.get_expiry_age())
|
||||
if must_create and not result:
|
||||
raise CreateError
|
||||
|
||||
def exists(self, session_key):
|
||||
return (
|
||||
bool(session_key) and (self.cache_key_prefix + session_key) in self._cache
|
||||
)
|
||||
return bool(session_key) and (self.cache_key_prefix + session_key) in self._cache
|
||||
|
||||
def delete(self, session_key=None):
|
||||
if session_key is None:
|
||||
|
||||
@@ -13,7 +13,6 @@ class SessionStore(DBStore):
|
||||
"""
|
||||
Implement cached, database backed sessions.
|
||||
"""
|
||||
|
||||
cache_key_prefix = KEY_PREFIX
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
@@ -36,19 +35,13 @@ class SessionStore(DBStore):
|
||||
s = self._get_session_from_db()
|
||||
if s:
|
||||
data = self.decode(s.session_data)
|
||||
self._cache.set(
|
||||
self.cache_key, data, self.get_expiry_age(expiry=s.expire_date)
|
||||
)
|
||||
self._cache.set(self.cache_key, data, self.get_expiry_age(expiry=s.expire_date))
|
||||
else:
|
||||
data = {}
|
||||
return data
|
||||
|
||||
def exists(self, session_key):
|
||||
return (
|
||||
session_key
|
||||
and (self.cache_key_prefix + session_key) in self._cache
|
||||
or super().exists(session_key)
|
||||
)
|
||||
return session_key and (self.cache_key_prefix + session_key) in self._cache or super().exists(session_key)
|
||||
|
||||
def save(self, must_create=False):
|
||||
super().save(must_create)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
|
||||
from django.contrib.sessions.backends.base import (
|
||||
CreateError, SessionBase, UpdateError,
|
||||
)
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.db import DatabaseError, IntegrityError, router, transaction
|
||||
from django.utils import timezone
|
||||
@@ -11,7 +13,6 @@ class SessionStore(SessionBase):
|
||||
"""
|
||||
Implement database session store.
|
||||
"""
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
super().__init__(session_key)
|
||||
|
||||
@@ -20,7 +21,6 @@ class SessionStore(SessionBase):
|
||||
# Avoids a circular import and allows importing SessionStore when
|
||||
# django.contrib.sessions is not in INSTALLED_APPS.
|
||||
from django.contrib.sessions.models import Session
|
||||
|
||||
return Session
|
||||
|
||||
@cached_property
|
||||
@@ -30,11 +30,12 @@ class SessionStore(SessionBase):
|
||||
def _get_session_from_db(self):
|
||||
try:
|
||||
return self.model.objects.get(
|
||||
session_key=self.session_key, expire_date__gt=timezone.now()
|
||||
session_key=self.session_key,
|
||||
expire_date__gt=timezone.now()
|
||||
)
|
||||
except (self.model.DoesNotExist, SuspiciousOperation) as e:
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger("django.security.%s" % e.__class__.__name__)
|
||||
logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
self._session_key = None
|
||||
|
||||
@@ -83,9 +84,7 @@ class SessionStore(SessionBase):
|
||||
using = router.db_for_write(self.model, instance=obj)
|
||||
try:
|
||||
with transaction.atomic(using=using):
|
||||
obj.save(
|
||||
force_insert=must_create, force_update=not must_create, using=using
|
||||
)
|
||||
obj.save(force_insert=must_create, force_update=not must_create, using=using)
|
||||
except IntegrityError:
|
||||
if must_create:
|
||||
raise CreateError
|
||||
|
||||
@@ -6,10 +6,7 @@ import tempfile
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import (
|
||||
VALID_KEY_CHARS,
|
||||
CreateError,
|
||||
SessionBase,
|
||||
UpdateError,
|
||||
VALID_KEY_CHARS, CreateError, SessionBase, UpdateError,
|
||||
)
|
||||
from django.contrib.sessions.exceptions import InvalidSessionKey
|
||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
||||
@@ -20,7 +17,6 @@ class SessionStore(SessionBase):
|
||||
"""
|
||||
Implement a file based session store.
|
||||
"""
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
self.storage_path = self._get_storage_path()
|
||||
self.file_prefix = settings.SESSION_COOKIE_NAME
|
||||
@@ -31,16 +27,13 @@ class SessionStore(SessionBase):
|
||||
try:
|
||||
return cls._storage_path
|
||||
except AttributeError:
|
||||
storage_path = (
|
||||
getattr(settings, "SESSION_FILE_PATH", None) or tempfile.gettempdir()
|
||||
)
|
||||
storage_path = getattr(settings, 'SESSION_FILE_PATH', None) or tempfile.gettempdir()
|
||||
# Make sure the storage path is valid.
|
||||
if not os.path.isdir(storage_path):
|
||||
raise ImproperlyConfigured(
|
||||
"The session storage path %r doesn't exist. Please set your"
|
||||
" SESSION_FILE_PATH setting to an existing directory in which"
|
||||
" Django can store session data." % storage_path
|
||||
)
|
||||
" Django can store session data." % storage_path)
|
||||
|
||||
cls._storage_path = storage_path
|
||||
return storage_path
|
||||
@@ -56,7 +49,8 @@ class SessionStore(SessionBase):
|
||||
# should always be md5s, so they should never contain directory
|
||||
# components.
|
||||
if not set(session_key).issubset(VALID_KEY_CHARS):
|
||||
raise InvalidSessionKey("Invalid characters in session key")
|
||||
raise InvalidSessionKey(
|
||||
"Invalid characters in session key")
|
||||
|
||||
return os.path.join(self.storage_path, self.file_prefix + session_key)
|
||||
|
||||
@@ -65,22 +59,23 @@ class SessionStore(SessionBase):
|
||||
Return the modification time of the file storing the session's content.
|
||||
"""
|
||||
modification = os.stat(self._key_to_file()).st_mtime
|
||||
tz = timezone.utc if settings.USE_TZ else None
|
||||
return datetime.datetime.fromtimestamp(modification, tz=tz)
|
||||
if settings.USE_TZ:
|
||||
modification = datetime.datetime.utcfromtimestamp(modification)
|
||||
return modification.replace(tzinfo=timezone.utc)
|
||||
return datetime.datetime.fromtimestamp(modification)
|
||||
|
||||
def _expiry_date(self, session_data):
|
||||
"""
|
||||
Return the expiry time of the file storing the session's content.
|
||||
"""
|
||||
return session_data.get("_session_expiry") or (
|
||||
self._last_modification()
|
||||
+ datetime.timedelta(seconds=self.get_session_cookie_age())
|
||||
return session_data.get('_session_expiry') or (
|
||||
self._last_modification() + datetime.timedelta(seconds=self.get_session_cookie_age())
|
||||
)
|
||||
|
||||
def load(self):
|
||||
session_data = {}
|
||||
try:
|
||||
with open(self._key_to_file(), encoding="ascii") as session_file:
|
||||
with open(self._key_to_file(), encoding='ascii') as session_file:
|
||||
file_data = session_file.read()
|
||||
# Don't fail if there is no data in the session file.
|
||||
# We may have opened the empty placeholder file.
|
||||
@@ -89,9 +84,7 @@ class SessionStore(SessionBase):
|
||||
session_data = self.decode(file_data)
|
||||
except (EOFError, SuspiciousOperation) as e:
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger(
|
||||
"django.security.%s" % e.__class__.__name__
|
||||
)
|
||||
logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
self.create()
|
||||
|
||||
@@ -127,7 +120,7 @@ class SessionStore(SessionBase):
|
||||
try:
|
||||
# Make sure the file exists. If it does not already exist, an
|
||||
# empty placeholder file is created.
|
||||
flags = os.O_WRONLY | getattr(os, "O_BINARY", 0)
|
||||
flags = os.O_WRONLY | getattr(os, 'O_BINARY', 0)
|
||||
if must_create:
|
||||
flags |= os.O_EXCL | os.O_CREAT
|
||||
fd = os.open(session_file_name, flags)
|
||||
@@ -157,9 +150,7 @@ class SessionStore(SessionBase):
|
||||
dir, prefix = os.path.split(session_file_name)
|
||||
|
||||
try:
|
||||
output_file_fd, output_file_name = tempfile.mkstemp(
|
||||
dir=dir, prefix=prefix + "_out_"
|
||||
)
|
||||
output_file_fd, output_file_name = tempfile.mkstemp(dir=dir, prefix=prefix + '_out_')
|
||||
renamed = False
|
||||
try:
|
||||
try:
|
||||
@@ -202,7 +193,7 @@ class SessionStore(SessionBase):
|
||||
for session_file in os.listdir(storage_path):
|
||||
if not session_file.startswith(file_prefix):
|
||||
continue
|
||||
session_key = session_file[len(file_prefix) :]
|
||||
session_key = session_file[len(file_prefix):]
|
||||
session = cls(session_key)
|
||||
# When an expired session is loaded, its file is removed, and a
|
||||
# new file is immediately created. Prevent this by disabling
|
||||
|
||||
@@ -3,6 +3,7 @@ from django.core import signing
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the data from the key itself instead of fetching from some
|
||||
@@ -15,7 +16,7 @@ class SessionStore(SessionBase):
|
||||
serializer=self.serializer,
|
||||
# This doesn't handle non-default expiry dates, see #19201
|
||||
max_age=self.get_session_cookie_age(),
|
||||
salt="django.contrib.sessions.backends.signed_cookies",
|
||||
salt='django.contrib.sessions.backends.signed_cookies',
|
||||
)
|
||||
except Exception:
|
||||
# BadSignature, ValueError, or unpickling exceptions. If any of
|
||||
@@ -53,7 +54,7 @@ class SessionStore(SessionBase):
|
||||
and set the modified flag so that the cookie is set on the client for
|
||||
the current request.
|
||||
"""
|
||||
self._session_key = ""
|
||||
self._session_key = ''
|
||||
self._session_cache = {}
|
||||
self.modified = True
|
||||
|
||||
@@ -70,9 +71,8 @@ class SessionStore(SessionBase):
|
||||
base64-encoded string of data as our session key.
|
||||
"""
|
||||
return signing.dumps(
|
||||
self._session,
|
||||
compress=True,
|
||||
salt="django.contrib.sessions.backends.signed_cookies",
|
||||
self._session, compress=True,
|
||||
salt='django.contrib.sessions.backends.signed_cookies',
|
||||
serializer=self.serializer,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user