86 lines
3.1 KiB
Python
86 lines
3.1 KiB
Python
"""Utilities for handling reCAPTCHA."""
|
|
from typing import Tuple, Optional
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
from django.http.request import validate_host
|
|
import requests
|
|
|
|
logger = logging.getLogger(__name__)
|
|
recaptcha_session = requests.Session()
|
|
recaptcha_session.mount(
|
|
'https://',
|
|
requests.adapters.HTTPAdapter(
|
|
max_retries=requests.adapters.Retry(total=10, backoff_factor=0.2),
|
|
),
|
|
)
|
|
VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'
|
|
MIN_SCORE = 0.5
|
|
msg_recaptcha_unavailable = (
|
|
'There was a communication error checking reCAPTCHA. Please try again later.'
|
|
)
|
|
|
|
|
|
def check_recaptcha(request, recaptcha_response) -> Tuple[Optional[bool], str]:
|
|
"""Handles recaptcha verification and sets request.recaptcha_is_valid.
|
|
|
|
Be sure to set settings.GOOGLE_RECAPTCHA_SECRET_KEY to a non-empty string,
|
|
otherwise this function does nothing.
|
|
|
|
:returns: None when no check was performed, or True/False indicating a
|
|
successful resp. unsuccessful check.
|
|
"""
|
|
if not recaptcha_response:
|
|
logger.warning('reCAPTCHA response not included in request')
|
|
return (
|
|
False,
|
|
'reCAPTCHA failed to check your request. Please disable script/ad '
|
|
'blockers and try again.',
|
|
)
|
|
|
|
data = {'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY, 'response': recaptcha_response}
|
|
full_url = request.build_absolute_uri()
|
|
try:
|
|
r = recaptcha_session.post(VERIFY_URL, data=data)
|
|
except (IOError, OSError):
|
|
logger.exception('Error communicating with Google reCAPTCHA service at %s', full_url)
|
|
return None, msg_recaptcha_unavailable
|
|
|
|
if r.status_code != 200:
|
|
logger.error("Error code %d verifying reCAPTCHA at %s: %s", r.status_code, full_url, r.text)
|
|
return None, msg_recaptcha_unavailable
|
|
|
|
result = r.json()
|
|
|
|
if not result.get('success', False):
|
|
logger.debug("reCAPTCHA failed to verify at %s: %s", full_url, r.text)
|
|
message = 'reCAPTCHA failed to verify that you are human being. Please try again.'
|
|
error_codes = result.get('error-codes') or []
|
|
if 'timeout-or-duplicate' in error_codes:
|
|
message = (
|
|
'reCAPTCHA token expired or was already used.'
|
|
' Please reload the page and try again.'
|
|
)
|
|
return False, message
|
|
|
|
# Check that nobody is trying to fake the response via some other website.
|
|
hostname = result.get('hostname')
|
|
if not validate_host(hostname, settings.ALLOWED_HOSTS):
|
|
logger.error(
|
|
"reCAPTCHA verified but for an unexpected hostname %r at %s",
|
|
hostname,
|
|
full_url,
|
|
)
|
|
return (
|
|
False,
|
|
'reCAPTCHA verified, but for an unexpected hostname. Please try again. If '
|
|
'this keeps happening, send an email to production@blender.org.',
|
|
)
|
|
|
|
score = result.get('score', 0.0)
|
|
if not score or score < MIN_SCORE:
|
|
logger.debug('score: %s, expected minimum: %s, %s', score, MIN_SCORE, result)
|
|
return False, 'reCAPTCHA failed to verify that you are human being. Please try again.'
|
|
|
|
return True, ''
|