76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
import json
|
|
from urllib import request as http
|
|
from urllib.parse import urlencode
|
|
|
|
from flask import current_app
|
|
from flask import request
|
|
from wtforms import ValidationError
|
|
|
|
RECAPTCHA_VERIFY_SERVER_DEFAULT = "https://www.google.com/recaptcha/api/siteverify"
|
|
RECAPTCHA_ERROR_CODES = {
|
|
"missing-input-secret": "The secret parameter is missing.",
|
|
"invalid-input-secret": "The secret parameter is invalid or malformed.",
|
|
"missing-input-response": "The response parameter is missing.",
|
|
"invalid-input-response": "The response parameter is invalid or malformed.",
|
|
}
|
|
|
|
|
|
__all__ = ["Recaptcha"]
|
|
|
|
|
|
class Recaptcha:
|
|
"""Validates a ReCaptcha."""
|
|
|
|
def __init__(self, message=None):
|
|
if message is None:
|
|
message = RECAPTCHA_ERROR_CODES["missing-input-response"]
|
|
self.message = message
|
|
|
|
def __call__(self, form, field):
|
|
if current_app.testing:
|
|
return True
|
|
|
|
if request.is_json:
|
|
response = request.json.get("g-recaptcha-response", "")
|
|
else:
|
|
response = request.form.get("g-recaptcha-response", "")
|
|
remote_ip = request.remote_addr
|
|
|
|
if not response:
|
|
raise ValidationError(field.gettext(self.message))
|
|
|
|
if not self._validate_recaptcha(response, remote_ip):
|
|
field.recaptcha_error = "incorrect-captcha-sol"
|
|
raise ValidationError(field.gettext(self.message))
|
|
|
|
def _validate_recaptcha(self, response, remote_addr):
|
|
"""Performs the actual validation."""
|
|
try:
|
|
private_key = current_app.config["RECAPTCHA_PRIVATE_KEY"]
|
|
except KeyError:
|
|
raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set") from None
|
|
|
|
verify_server = current_app.config.get("RECAPTCHA_VERIFY_SERVER")
|
|
if not verify_server:
|
|
verify_server = RECAPTCHA_VERIFY_SERVER_DEFAULT
|
|
|
|
data = urlencode(
|
|
{"secret": private_key, "remoteip": remote_addr, "response": response}
|
|
)
|
|
|
|
http_response = http.urlopen(verify_server, data.encode("utf-8"))
|
|
|
|
if http_response.code != 200:
|
|
return False
|
|
|
|
json_resp = json.loads(http_response.read())
|
|
|
|
if json_resp["success"]:
|
|
return True
|
|
|
|
for error in json_resp.get("error-codes", []):
|
|
if error in RECAPTCHA_ERROR_CODES:
|
|
raise ValidationError(RECAPTCHA_ERROR_CODES[error])
|
|
|
|
return False
|