sessions.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. from hashlib import md5
  2. from datetime import timedelta
  3. from django.conf import settings
  4. from django.contrib.sessions.backends.base import SessionBase, CreateError
  5. from django.db import IntegrityError
  6. from django.db.models.loading import cache as model_cache
  7. from django.utils import timezone
  8. from django.utils.crypto import salted_hmac
  9. from django.utils.encoding import force_unicode
  10. from misago.auth import auth_remember, AuthException
  11. from misago.models import Session, Token, Guest, User
  12. from misago.utils.strings import random_string
  13. # Assert models are loaded
  14. if not model_cache.loaded:
  15. model_cache.get_models()
  16. class IncorrectSessionException(Exception):
  17. pass
  18. class MisagoSession(SessionBase):
  19. """
  20. Abstract class for sessions to inherit and extend
  21. """
  22. def _get_new_session_key(self):
  23. return random_string(42)
  24. def _get_session(self):
  25. try:
  26. return self._session_cache
  27. except AttributeError:
  28. self._session_cache = self.load()
  29. return self._session_cache
  30. def _hash(self, value):
  31. key_salt = "misago.sessions" + self.__class__.__name__
  32. return salted_hmac(key_salt, value).hexdigest()
  33. def delete(self):
  34. """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
  35. pass
  36. def created(self):
  37. try:
  38. return self.started
  39. except AttributeError:
  40. return False
  41. def flush(self):
  42. """We use sessions to track onlines so sorry, only sessions cleaner may delete sessions"""
  43. pass
  44. def load(self):
  45. return self.decode(force_unicode(self._session_rk.data))
  46. def session_expired(self):
  47. return False
  48. def get_ip(self, request):
  49. return request.META.get('HTTP_X_FORWARDED_FOR', '') or request.META.get('REMOTE_ADDR')
  50. def set_user(self, user=None):
  51. pass
  52. def get_ban(self):
  53. return False
  54. def set_ban(self, ban):
  55. return False
  56. def save(self):
  57. self._session_rk.data = self.encode(self._get_session())
  58. self._session_rk.last = timezone.now()
  59. if self._session_rk.pk:
  60. self._session_rk.save(force_update=True)
  61. else:
  62. self._session_rk.save(force_insert=True)
  63. class CrawlerSession(MisagoSession):
  64. """
  65. Crawler Session controller
  66. """
  67. def __init__(self, request):
  68. self.started = False
  69. self.team = False
  70. self._ip = self.get_ip(request)
  71. self._session_key = md5('%s-%s' % (request.user.username, self._ip)).hexdigest()
  72. try:
  73. self._session_rk = Session.objects.get(id=self._session_key)
  74. self._session_key = self._session_rk.id
  75. except Session.DoesNotExist:
  76. self.create(request)
  77. def create(self, request):
  78. self._session_rk = Session(
  79. id=self._session_key,
  80. data=self.encode({}),
  81. crawler=request.user.username,
  82. ip=self._ip,
  83. agent=request.META.get('HTTP_USER_AGENT', ''),
  84. start=timezone.now(),
  85. last=timezone.now(),
  86. matched=True
  87. )
  88. while True:
  89. try:
  90. self._session_rk.save(force_insert=True)
  91. break
  92. except CreateError:
  93. continue
  94. except IntegrityError:
  95. try:
  96. self._session_rk = Session.objects.get(id=self._session_key)
  97. except Session.DoesNotExist:
  98. continue
  99. def human_session(self):
  100. return False
  101. class HumanSession(MisagoSession):
  102. """
  103. Human Session controller
  104. """
  105. def __init__(self, request):
  106. self.started = False
  107. self.expired = False
  108. self.team = False
  109. self.rank = None
  110. self.remember_me = None
  111. self._user = None
  112. self._ip = self.get_ip(request)
  113. self._session_token = None
  114. if request.firewall.admin:
  115. self._cookie_sid = settings.COOKIES_PREFIX + 'ASID'
  116. else:
  117. self._cookie_sid = settings.COOKIES_PREFIX + 'SID'
  118. try:
  119. # Do we have correct session ID?
  120. if self._cookie_sid not in request.COOKIES or len(request.COOKIES[self._cookie_sid]) != 42:
  121. raise IncorrectSessionException()
  122. self._session_key = request.COOKIES[self._cookie_sid]
  123. self._session_rk = Session.objects.select_related().get(
  124. pk=self._session_key,
  125. admin=request.firewall.admin
  126. )
  127. # IP invalid
  128. if request.settings.sessions_validate_ip and self._session_rk.ip != self._ip:
  129. raise IncorrectSessionException()
  130. # Session expired
  131. if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
  132. self.expired = True
  133. raise IncorrectSessionException()
  134. # Change session to matched and extract session user
  135. if not self._session_rk.matched:
  136. self.started = True
  137. self._session_rk.matched = True
  138. self._user = self._session_rk.user
  139. self.team = self._session_rk.team
  140. except (Session.DoesNotExist, IncorrectSessionException):
  141. # Attempt autolog
  142. try:
  143. self.remember_me = auth_remember(request, self.get_ip(request))
  144. self.create(request, user=self.remember_me.user)
  145. self.started = True
  146. except AuthException as e:
  147. # Autolog failed
  148. self.create(request)
  149. self.id = self._session_rk.id
  150. # Make cookie live longer
  151. if request.firewall.admin:
  152. request.cookiejar.set('ASID', self._session_rk.id)
  153. else:
  154. request.cookiejar.set('SID', self._session_rk.id)
  155. def create(self, request, user=None):
  156. self._user = user
  157. while True:
  158. try:
  159. self._session_key = self._get_new_session_key()
  160. self._session_rk = Session(
  161. id=self._session_key,
  162. data=self.encode({}),
  163. user=self._user,
  164. ip=self._ip,
  165. agent=request.META.get('HTTP_USER_AGENT', ''),
  166. start=timezone.now(),
  167. last=timezone.now(),
  168. admin=request.firewall.admin,
  169. )
  170. self._session_rk.save(force_insert=True)
  171. if user:
  172. # Update user data
  173. user.set_last_visit(
  174. self.get_ip(request),
  175. request.META.get('HTTP_USER_AGENT', '')
  176. )
  177. user.save(force_update=True)
  178. break
  179. except CreateError:
  180. # Key wasn't unique. Try again.
  181. continue
  182. def save(self):
  183. self._session_rk.user = self._user
  184. self._session_rk.team = self.team
  185. self._session_rk.rank_id = self.rank
  186. super(HumanSession, self).save()
  187. def human_session(self):
  188. return True
  189. def session_expired(self):
  190. return self.expired
  191. def get_user(self):
  192. if self._user == None:
  193. return Guest()
  194. return self._user
  195. def set_user(self, user=None):
  196. self._user = user
  197. def sign_out(self, request):
  198. try:
  199. if self._user.is_authenticated():
  200. if not request.firewall.admin:
  201. cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
  202. if cookie_token in request.COOKIES:
  203. if len(request.COOKIES[cookie_token]) > 0:
  204. Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
  205. request.cookiejar.delete('TOKEN')
  206. self._user = None
  207. request.user = Guest()
  208. except AttributeError:
  209. pass
  210. class SessionMock(object):
  211. def get_ip(self, request):
  212. try:
  213. return self.ip
  214. except AttributeError:
  215. return '127.0.0.1'