sessions.py 9.4 KB

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