sessions.py 8.8 KB

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