sessions.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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().get(
  128. pk=self._session_key,
  129. admin=request.firewall.admin
  130. )
  131. # IP invalid
  132. if request.settings.sessions_validate_ip and self._session_rk.ip != self._ip:
  133. raise IncorrectSessionException()
  134. # Session expired
  135. if timezone.now() - self._session_rk.last > timedelta(seconds=settings.SESSION_LIFETIME):
  136. self.expired = True
  137. raise IncorrectSessionException()
  138. # Change session to matched and extract session user
  139. if self._session_rk.matched:
  140. self.matched = True
  141. else:
  142. self.started = True
  143. self._user = self._session_rk.user
  144. self.team = self._session_rk.team
  145. except (Session.DoesNotExist, IncorrectSessionException):
  146. # Attempt autolog
  147. try:
  148. self.remember_me = auth_remember(request, self.get_ip(request))
  149. self.create(request, user=self.remember_me.user)
  150. self.started = True
  151. self._session_rk.matched = True
  152. except AuthException as e:
  153. # Autolog failed
  154. self.create(request)
  155. self.id = self._session_rk.id
  156. # Make cookie live longer
  157. if request.firewall.admin:
  158. request.cookiejar.set('ASID', self._session_rk.id)
  159. else:
  160. request.cookiejar.set('SID', self._session_rk.id)
  161. def create(self, request, user=None):
  162. self._user = user
  163. while True:
  164. try:
  165. self._session_key = self._get_new_session_key()
  166. self._session_rk = Session(
  167. id=self._session_key,
  168. data=self.encode({}),
  169. user=self._user,
  170. ip=self._ip,
  171. agent=request.META.get('HTTP_USER_AGENT', ''),
  172. start=timezone.now(),
  173. last=timezone.now(),
  174. admin=request.firewall.admin,
  175. )
  176. self._session_rk.save(force_insert=True)
  177. if user:
  178. # Update user data
  179. user.set_last_visit(
  180. self.get_ip(request),
  181. request.META.get('HTTP_USER_AGENT', '')
  182. )
  183. user.save(force_update=True)
  184. break
  185. except CreateError:
  186. # Key wasn't unique. Try again.
  187. continue
  188. def save(self):
  189. self._session_rk.user = self._user
  190. self._session_rk.team = self.team
  191. self._session_rk.rank_id = self.rank
  192. super(HumanSession, self).save()
  193. def human_session(self):
  194. return True
  195. def session_expired(self):
  196. return self.expired
  197. def get_user(self):
  198. if self._user == None:
  199. return Guest()
  200. return self._user
  201. def set_user(self, user=None):
  202. self._user = user
  203. def sign_out(self, request):
  204. try:
  205. if self._user.is_authenticated():
  206. if not request.firewall.admin:
  207. cookie_token = settings.COOKIES_PREFIX + 'TOKEN'
  208. if cookie_token in request.COOKIES:
  209. if len(request.COOKIES[cookie_token]) > 0:
  210. Token.objects.filter(id=request.COOKIES[cookie_token]).delete()
  211. request.cookiejar.delete('TOKEN')
  212. self._user = None
  213. request.user = Guest()
  214. except AttributeError:
  215. pass
  216. class SessionMock(object):
  217. def get_ip(self, request):
  218. try:
  219. return self.ip
  220. except AttributeError:
  221. return '127.0.0.1'