You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import logging
  2. from django.contrib.sessions.backends.base import (
  3. CreateError, SessionBase, UpdateError,
  4. )
  5. from django.core.exceptions import SuspiciousOperation
  6. from django.db import DatabaseError, IntegrityError, router, transaction
  7. from django.utils import timezone
  8. from django.utils.functional import cached_property
  9. class SessionStore(SessionBase):
  10. """
  11. Implement database session store.
  12. """
  13. def __init__(self, session_key=None):
  14. super().__init__(session_key)
  15. @classmethod
  16. def get_model_class(cls):
  17. # Avoids a circular import and allows importing SessionStore when
  18. # django.contrib.sessions is not in INSTALLED_APPS.
  19. from django.contrib.sessions.models import Session
  20. return Session
  21. @cached_property
  22. def model(self):
  23. return self.get_model_class()
  24. def _get_session_from_db(self):
  25. try:
  26. return self.model.objects.get(
  27. session_key=self.session_key,
  28. expire_date__gt=timezone.now()
  29. )
  30. except (self.model.DoesNotExist, SuspiciousOperation) as e:
  31. if isinstance(e, SuspiciousOperation):
  32. logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
  33. logger.warning(str(e))
  34. self._session_key = None
  35. def load(self):
  36. s = self._get_session_from_db()
  37. return self.decode(s.session_data) if s else {}
  38. def exists(self, session_key):
  39. return self.model.objects.filter(session_key=session_key).exists()
  40. def create(self):
  41. while True:
  42. self._session_key = self._get_new_session_key()
  43. try:
  44. # Save immediately to ensure we have a unique entry in the
  45. # database.
  46. self.save(must_create=True)
  47. except CreateError:
  48. # Key wasn't unique. Try again.
  49. continue
  50. self.modified = True
  51. return
  52. def create_model_instance(self, data):
  53. """
  54. Return a new instance of the session model object, which represents the
  55. current session state. Intended to be used for saving the session data
  56. to the database.
  57. """
  58. return self.model(
  59. session_key=self._get_or_create_session_key(),
  60. session_data=self.encode(data),
  61. expire_date=self.get_expiry_date(),
  62. )
  63. def save(self, must_create=False):
  64. """
  65. Save the current session data to the database. If 'must_create' is
  66. True, raise a database error if the saving operation doesn't create a
  67. new entry (as opposed to possibly updating an existing entry).
  68. """
  69. if self.session_key is None:
  70. return self.create()
  71. data = self._get_session(no_load=must_create)
  72. obj = self.create_model_instance(data)
  73. using = router.db_for_write(self.model, instance=obj)
  74. try:
  75. with transaction.atomic(using=using):
  76. obj.save(force_insert=must_create, force_update=not must_create, using=using)
  77. except IntegrityError:
  78. if must_create:
  79. raise CreateError
  80. raise
  81. except DatabaseError:
  82. if not must_create:
  83. raise UpdateError
  84. raise
  85. def delete(self, session_key=None):
  86. if session_key is None:
  87. if self.session_key is None:
  88. return
  89. session_key = self.session_key
  90. try:
  91. self.model.objects.get(session_key=session_key).delete()
  92. except self.model.DoesNotExist:
  93. pass
  94. @classmethod
  95. def clear_expired(cls):
  96. cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()