ban.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import re
  2. from django.conf import settings
  3. from django.db import models
  4. from django.utils import timezone
  5. from django.utils.translation import ugettext_lazy as _, ungettext, pgettext
  6. from misago.core import cachebuster
  7. from misago.core.utils import date_format
  8. __all__ = [
  9. 'BAN_USERNAME', 'BAN_EMAIL', 'BAN_IP', 'BANS_CHOICES',
  10. 'Ban', 'BanCache', 'format_expiration_date'
  11. ]
  12. BAN_CACHEBUSTER = 'misago_bans'
  13. BAN_USERNAME = 0
  14. BAN_EMAIL = 1
  15. BAN_IP = 2
  16. BANS_CHOICES = (
  17. (BAN_USERNAME, _('Username')),
  18. (BAN_EMAIL, _('E-mail address')),
  19. (BAN_IP, _('IP address')),
  20. )
  21. def format_expiration_date(expiration_date):
  22. if not expiration_date:
  23. return _("Never")
  24. now = timezone.now()
  25. diff = (expiration_date - now).total_seconds()
  26. if diff and diff < (3600 * 24):
  27. format = pgettext("ban expiration hour minute",
  28. "h:i a")
  29. elif now.year == expiration_date.year:
  30. format = pgettext("ban expiration hour minute day month",
  31. "jS F, h:i a")
  32. else:
  33. format = pgettext("ban expiration hour minute day month year",
  34. "jS F Y, h:i a")
  35. return date_format(expiration_date, format)
  36. class BansManager(models.Manager):
  37. def get_ip_ban(self, ip):
  38. return self.get_ban(ip=ip)
  39. def get_username_ban(self, username):
  40. return self.get_ban(username=username)
  41. def get_email_ban(self, email):
  42. return self.get_ban(email=email)
  43. def invalidate_cache(self):
  44. cachebuster.invalidate(BAN_CACHEBUSTER)
  45. def get_ban(self, username=None, email=None, ip=None):
  46. checks = []
  47. if username:
  48. username = username.lower()
  49. checks.append(BAN_USERNAME)
  50. if email:
  51. email = email.lower()
  52. checks.append(BAN_EMAIL)
  53. if ip:
  54. checks.append(BAN_IP)
  55. queryset = self.filter(is_checked=True)
  56. if len(checks) == 1:
  57. queryset = queryset.filter(check_type=checks[0])
  58. elif checks:
  59. queryset = queryset.filter(check_type__in=checks)
  60. for ban in queryset.order_by('-id').iterator():
  61. if (ban.check_type == BAN_USERNAME and username and
  62. ban.check_value(username)):
  63. return ban
  64. elif (ban.check_type == BAN_EMAIL and email and
  65. ban.check_value(email)):
  66. return ban
  67. elif ban.check_type == BAN_IP and ip and ban.check_value(ip):
  68. return ban
  69. else:
  70. raise Ban.DoesNotExist('specified values are not banned')
  71. class Ban(models.Model):
  72. check_type = models.PositiveIntegerField(default=BAN_USERNAME, db_index=True)
  73. banned_value = models.CharField(max_length=255, db_index=True)
  74. user_message = models.TextField(null=True, blank=True)
  75. staff_message = models.TextField(null=True, blank=True)
  76. expires_on = models.DateTimeField(null=True, blank=True, db_index=True)
  77. is_checked = models.BooleanField(default=True, db_index=True)
  78. objects = BansManager()
  79. def save(self, *args, **kwargs):
  80. self.banned_value = self.banned_value.lower()
  81. self.is_checked = not self.is_expired
  82. return super(Ban, self).save(*args, **kwargs)
  83. @property
  84. def check_name(self):
  85. return BANS_CHOICES[self.check_type][1]
  86. @property
  87. def name(self):
  88. return self.banned_value
  89. @property
  90. def is_expired(self):
  91. if self.expires_on:
  92. return self.expires_on < timezone.now()
  93. else:
  94. return False
  95. @property
  96. def formatted_expiration_date(self):
  97. return format_expiration_date(self.expires_on)
  98. def check_value(self, value):
  99. if '*' in self.banned_value:
  100. regex = re.escape(self.banned_value).replace('\*', '(.*?)')
  101. return re.search('^%s$' % regex, value) is not None
  102. else:
  103. return self.banned_value == value
  104. def lift(self):
  105. self.expires_on = timezone.now()
  106. class BanCache(models.Model):
  107. user = models.OneToOneField(
  108. settings.AUTH_USER_MODEL, primary_key=True, related_name='ban_cache')
  109. ban = models.ForeignKey(
  110. Ban, null=True, blank=True, on_delete=models.SET_NULL)
  111. bans_version = models.PositiveIntegerField(default=0)
  112. user_message = models.TextField(null=True, blank=True)
  113. staff_message = models.TextField(null=True, blank=True)
  114. expires_on = models.DateTimeField(null=True, blank=True)
  115. @property
  116. def formatted_expiration_date(self):
  117. return format_expiration_date(self.expires_on)
  118. @property
  119. def is_banned(self):
  120. return bool(self.ban)
  121. @property
  122. def is_valid(self):
  123. version_is_valid = cachebuster.is_valid(BAN_CACHEBUSTER,
  124. self.bans_version)
  125. expired = self.expires_on and self.expires_on < timezone.now()
  126. return version_is_valid and not expired