validators.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import json
  2. import logging
  3. import re
  4. import requests
  5. from django.contrib.auth import get_user_model
  6. from django.core.exceptions import ValidationError
  7. from django.core.validators import validate_email as validate_email_content
  8. from django.utils.encoding import force_str
  9. from django.utils.module_loading import import_string
  10. from django.utils.translation import gettext_lazy as _
  11. from django.utils.translation import ngettext
  12. from requests.exceptions import RequestException
  13. from .. import hooks
  14. from ..conf import settings
  15. from .bans import get_email_ban, get_username_ban
  16. USERNAME_RE = re.compile(r"^[0-9a-z]+$", re.IGNORECASE)
  17. logger = logging.getLogger("misago.users.validators")
  18. User = get_user_model()
  19. # E-mail validators
  20. def validate_email(value, exclude=None):
  21. """shortcut function that does complete validation of email"""
  22. validate_email_content(value)
  23. validate_email_available(value, exclude)
  24. validate_email_banned(value)
  25. def validate_email_available(value, exclude=None):
  26. try:
  27. user = User.objects.get_by_email(value)
  28. if not exclude or user.pk != exclude.pk:
  29. raise ValidationError(_("This e-mail address is not available."))
  30. except User.DoesNotExist:
  31. pass
  32. def validate_email_banned(value):
  33. ban = get_email_ban(value, registration_only=True)
  34. if ban:
  35. if ban.user_message:
  36. raise ValidationError(ban.user_message)
  37. else:
  38. raise ValidationError(_("This e-mail address is not allowed."))
  39. # Username validators
  40. def validate_username(settings, value, exclude=None):
  41. """shortcut function that does complete validation of username"""
  42. validate_username_length(settings, value)
  43. validate_username_content(value)
  44. validate_username_available(value, exclude)
  45. validate_username_banned(value)
  46. def validate_username_available(value, exclude=None):
  47. try:
  48. user = User.objects.get_by_username(value)
  49. if not exclude or user.pk != exclude.pk:
  50. raise ValidationError(_("This username is not available."))
  51. except User.DoesNotExist:
  52. pass
  53. def validate_username_banned(value):
  54. ban = get_username_ban(value, registration_only=True)
  55. if ban:
  56. if ban.user_message:
  57. raise ValidationError(ban.user_message)
  58. else:
  59. raise ValidationError(_("This username is not allowed."))
  60. def validate_username_content(value):
  61. if not USERNAME_RE.match(value):
  62. raise ValidationError(
  63. _("Username can only contain latin alphabet letters and digits.")
  64. )
  65. def validate_username_length(settings, value):
  66. if len(value) < settings.username_length_min:
  67. message = ngettext(
  68. "Username must be at least %(limit_value)s character long.",
  69. "Username must be at least %(limit_value)s characters long.",
  70. settings.username_length_min,
  71. )
  72. raise ValidationError(message % {"limit_value": settings.username_length_min})
  73. if len(value) > settings.username_length_max:
  74. message = ngettext(
  75. "Username cannot be longer than %(limit_value)s characters.",
  76. "Username cannot be longer than %(limit_value)s characters.",
  77. settings.username_length_max,
  78. )
  79. raise ValidationError(message % {"limit_value": settings.username_length_max})
  80. # New account validators
  81. SFS_API_URL = (
  82. "http://api.stopforumspam.org/api?email=%(email)s&ip=%(ip)s&f=json&confidence"
  83. )
  84. def validate_with_sfs(request, cleaned_data, add_error):
  85. if request.settings.enable_stop_forum_spam and cleaned_data.get("email"):
  86. try:
  87. _real_validate_with_sfs(
  88. request.user_ip,
  89. cleaned_data["email"],
  90. request.settings.stop_forum_spam_confidence,
  91. )
  92. except RequestException as error:
  93. logger.exception(error, exc_info=error)
  94. def _real_validate_with_sfs(ip, email, confidence):
  95. r = requests.get(SFS_API_URL % {"email": email, "ip": ip}, timeout=5)
  96. r.raise_for_status()
  97. api_response = json.loads(force_str(r.content))
  98. ip_score = api_response.get("ip", {}).get("confidence", 0)
  99. email_score = api_response.get("email", {}).get("confidence", 0)
  100. api_score = max((ip_score, email_score))
  101. if api_score > confidence:
  102. raise ValidationError(_("Data entered was found in spammers database."))
  103. def validate_gmail_email(request, cleaned_data, add_error):
  104. email = cleaned_data.get("email", "")
  105. if "@" not in email:
  106. return
  107. username, domain = email.lower().split("@")
  108. if domain == "gmail.com" and username.count(".") > 5:
  109. add_error("email", ValidationError(_("This email is not allowed.")))
  110. # Registration validation
  111. validators_list = settings.MISAGO_NEW_REGISTRATIONS_VALIDATORS
  112. REGISTRATION_VALIDATORS = list(map(import_string, validators_list))
  113. def raise_validation_error(*_):
  114. raise ValidationError("") # Raised when message content can be discarded
  115. def validate_new_registration(request, cleaned_data, add_error=None, validators=None):
  116. validators = validators or REGISTRATION_VALIDATORS
  117. add_error = add_error or raise_validation_error
  118. for validator in validators:
  119. validator(request, cleaned_data, add_error)
  120. for validator in hooks.new_registrations_validators:
  121. validator(request, cleaned_data, add_error)