sessions.py 9.2 KB

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