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.
 
 
 
 

105 lines
3.6 KiB

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