sessions.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. def match(self):
  64. self._session_rk.matched = True
  65. class CrawlerSession(MisagoSession):
  66. """
  67. Crawler Session controller
  68. """
  69. def __init__(self, request):
  70. self.matched = True
  71. self.started = False
  72. self.team = False
  73. self._ip = self.get_ip(request)
  74. self._session_key = md5('%s-%s' % (request.user.username, self._ip)).hexdigest()
  75. try:
  76. self._session_rk = Session.objects.get(id=self._session_key)
  77. self._session_key = self._session_rk.id
  78. except Session.DoesNotExist:
  79. self.create(request)
  80. def create(self, request):
  81. self._session_rk = Session(
  82. id=self._session_key,
  83. data=self.encode({}),
  84. crawler=request.user.username,
  85. ip=self._ip,
  86. agent=request.META.get('HTTP_USER_AGENT', ''),
  87. start=timezone.now(),
  88. last=timezone.now(),
  89. matched=True
  90. )
  91. while True:
  92. try:
  93. self._session_rk.save(force_insert=True)
  94. break
  95. except CreateError:
  96. continue
  97. except IntegrityError:
  98. try:
  99. self._session_rk = Session.objects.get(id=self._session_key)
  100. except Session.DoesNotExist:
  101. continue
  102. def human_session(self):
  103. return False
  104. class HumanSession(MisagoSession):
  105. """
  106. Human Session controller
  107. """
  108. def __init__(self, request):
  109. self.started = False
  110. self.matched = False
  111. self.expired = False
  112. self.team = False
  113. self.rank = None
  114. self.remember_me = None
  115. self._user = None
  116. self._ip = self.get_ip(request)
  117. self._session_token = None
  118. if request.firewall.admin:
  119. self._cookie_sid = settings.COOKIES_PREFIX + 'ASID'
  120. else:
  121. self._cookie_sid = settings.COOKIES_PREFIX + 'SID'
  122. try:
  123. # Do we have correct session ID?
  124. if self._cookie_sid not in request.COOKIES or len(request.COOKIES[self._cookie_sid]) != 42:
  125. raise IncorrectSessionException()
  126. self._session_key = request.COOKIES[self._cookie_sid]
  127. self._session_rk = Session.objects.select_related('user', 'rank')
  128. if settings.USER_EXTENSIONS_PRELOAD:
  129. self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
  130. self._session_rk = self._session_rk.get(
  131. pk=self._session_key,
  132. admin=request.firewall.admin
  133. )
  134. # IP invalid
  135. if request.settings.sessions_validate_ip and self._session_rk.ip != self._ip:
  136. raise IncorrectSessionException()
  137. # Session expired
  138. if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
  139. self.expired = True
  140. raise IncorrectSessionException()
  141. # Change session to matched and extract session user
  142. if self._session_rk.matched:
  143. self.matched = True
  144. else:
  145. self.started = True
  146. self._user = self._session_rk.user
  147. self.team = self._session_rk.team
  148. except (Session.DoesNotExist, IncorrectSessionException):
  149. # Attempt autolog
  150. try:
  151. self.remember_me = auth_remember(request, self.get_ip(request))
  152. self.create(request, user=self.remember_me.user)
  153. self.started = True
  154. self._session_rk.matched = True
  155. except AuthException as e:
  156. # Autolog failed
  157. self.create(request)
  158. self.id = self._session_rk.id
  159. # Make cookie live longer
  160. if request.firewall.admin:
  161. request.cookiejar.set('ASID', self._session_rk.id)
  162. else:
  163. request.cookiejar.set('SID', self._session_rk.id)
  164. def create(self, request, user=None):
  165. self._user = user
  166. while True:
  167. try:
  168. self._session_key = self._get_new_session_key()
  169. self._session_rk = Session(
  170. id=self._session_key,
  171. data=self.encode({}),
  172. user=self._user,
  173. ip=self._ip,
  174. agent=request.META.get('HTTP_USER_AGENT', ''),
  175. start=timezone.now(),
  176. last=timezone.now(),
  177. admin=request.firewall.admin,
  178. )
  179. self._session_rk.save(force_insert=True)
  180. if settings.USER_EXTENSIONS_PRELOAD:
  181. self._session_rk = self._session_rk.select_related(*settings.USER_EXTENSIONS_PRELOAD)
  182. if user:
  183. # Update user data
  184. user.set_last_visit(
  185. self.get_ip(request),
  186. request.META.get('HTTP_USER_AGENT', '')
  187. )
  188. user.save(force_update=True)
  189. break
  190. except CreateError:
  191. # Key wasn't unique. Try again.
  192. continue
  193. def save(self):
  194. self._session_rk.user = self._user
  195. self._session_rk.team = self.team
  196. self._session_rk.rank_id = self.rank
  197. super(HumanSession, self).save()
  198. def human_session(self):
  199. return True
  200. def session_expired(self):
  201. return self.expired
  202. def get_user(self):
  203. if self._user == None:
  204. return Guest()
  205. return self._user
  206. def set_user(self, user=None):
  207. self._user = user
  208. def sign_out(self, request):
  209. try:
  210. if self._user.is_authenticated():
  211. if not request.firewall.admin:
  212. cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
  213. if cookie_token in request.COOKIES:
  214. if len(request.COOKIES[cookie_token]) > 0:
  215. Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
  216. request.cookiejar.delete('TOKEN')
  217. self._user = None
  218. request.user = Guest()
  219. except AttributeError:
  220. pass
  221. class SessionMock(object):
  222. def get_ip(self, request):
  223. try:
  224. return self.ip
  225. except AttributeError:
  226. return '127.0.0.1'