sessions.py 8.8 KB

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