validators.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import json
  2. from urllib import request as http
  3. from urllib.parse import urlencode
  4. from flask import current_app
  5. from flask import request
  6. from wtforms import ValidationError
  7. RECAPTCHA_VERIFY_SERVER_DEFAULT = "https://www.google.com/recaptcha/api/siteverify"
  8. RECAPTCHA_ERROR_CODES = {
  9. "missing-input-secret": "The secret parameter is missing.",
  10. "invalid-input-secret": "The secret parameter is invalid or malformed.",
  11. "missing-input-response": "The response parameter is missing.",
  12. "invalid-input-response": "The response parameter is invalid or malformed.",
  13. }
  14. __all__ = ["Recaptcha"]
  15. class Recaptcha:
  16. """Validates a ReCaptcha."""
  17. def __init__(self, message=None):
  18. if message is None:
  19. message = RECAPTCHA_ERROR_CODES["missing-input-response"]
  20. self.message = message
  21. def __call__(self, form, field):
  22. if current_app.testing:
  23. return True
  24. if request.is_json:
  25. response = request.json.get("g-recaptcha-response", "")
  26. else:
  27. response = request.form.get("g-recaptcha-response", "")
  28. remote_ip = request.remote_addr
  29. if not response:
  30. raise ValidationError(field.gettext(self.message))
  31. if not self._validate_recaptcha(response, remote_ip):
  32. field.recaptcha_error = "incorrect-captcha-sol"
  33. raise ValidationError(field.gettext(self.message))
  34. def _validate_recaptcha(self, response, remote_addr):
  35. """Performs the actual validation."""
  36. try:
  37. private_key = current_app.config["RECAPTCHA_PRIVATE_KEY"]
  38. except KeyError:
  39. raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set") from None
  40. verify_server = current_app.config.get("RECAPTCHA_VERIFY_SERVER")
  41. if not verify_server:
  42. verify_server = RECAPTCHA_VERIFY_SERVER_DEFAULT
  43. data = urlencode(
  44. {"secret": private_key, "remoteip": remote_addr, "response": response}
  45. )
  46. http_response = http.urlopen(verify_server, data.encode("utf-8"))
  47. if http_response.code != 200:
  48. return False
  49. json_resp = json.loads(http_response.read())
  50. if json_resp["success"]:
  51. return True
  52. for error in json_resp.get("error-codes", []):
  53. if error in RECAPTCHA_ERROR_CODES:
  54. raise ValidationError(RECAPTCHA_ERROR_CODES[error])
  55. return False