Files
fotohost/app/captcha_service.py
T

124 lines
4.0 KiB
Python

import requests
from app.settings_service import get_settings
CAPTCHA_PROVIDERS = ("none", "turnstile", "recaptcha_v2", "recaptcha_v3")
ENDPOINT_PAGE_MAP = {
"auth.login": "login",
"auth.register": "register",
"auth.forgot_password": "forgot_password",
}
def get_captcha_config(page):
settings = get_settings()
provider = (settings.captcha_provider or "none").strip()
if provider not in CAPTCHA_PROVIDERS or provider == "none":
return None
page_flags = {
"login": settings.captcha_on_login,
"register": settings.captcha_on_register,
"forgot_password": settings.captcha_on_forgot_password,
}
if not page_flags.get(page):
return None
config = {"provider": provider, "page": page}
if provider == "turnstile":
config["site_key"] = (settings.turnstile_site_key or "").strip()
elif provider == "recaptcha_v2":
config["site_key"] = (settings.recaptcha_v2_site_key or "").strip()
elif provider == "recaptcha_v3":
config["site_key"] = (settings.recaptcha_v3_site_key or "").strip()
config["action"] = page
if not config.get("site_key"):
return None
return config
def get_captcha_config_for_request(endpoint):
page = ENDPOINT_PAGE_MAP.get(endpoint)
if not page:
return None
return get_captcha_config(page)
def verify_captcha(request, page):
config = get_captcha_config(page)
if not config:
return True, None
settings = get_settings()
provider = config["provider"]
if provider == "turnstile":
token = (request.form.get("cf-turnstile-response") or "").strip()
secret = (settings.turnstile_secret_key or "").strip()
if not token:
return False, "Подтвердите captcha"
if not secret:
return False, "Captcha не настроена (нет secret key)"
return _verify_turnstile(token, secret)
token = (request.form.get("g-recaptcha-response") or "").strip()
if provider == "recaptcha_v2":
secret = (settings.recaptcha_v2_secret_key or "").strip()
if not token:
return False, "Подтвердите captcha"
if not secret:
return False, "Captcha не настроена (нет secret key)"
return _verify_recaptcha(token, secret)
secret = (settings.recaptcha_v3_secret_key or "").strip()
if not token:
return False, "Подтвердите captcha"
if not secret:
return False, "Captcha не настроена (нет secret key)"
ok, msg, score = _verify_recaptcha_with_score(token, secret)
if not ok:
return False, msg
min_score = settings.recaptcha_v3_min_score if settings.recaptcha_v3_min_score is not None else 0.5
if score < min_score:
return False, "Подозрительная активность, попробуйте снова"
return True, None
def _verify_turnstile(token, secret):
try:
response = requests.post(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
data={"secret": secret, "response": token},
timeout=10,
)
data = response.json()
except requests.RequestException:
return False, "Не удалось проверить captcha"
if data.get("success"):
return True, None
return False, "Captcha не пройдена"
def _verify_recaptcha(token, secret):
ok, msg, _score = _verify_recaptcha_with_score(token, secret)
return ok, msg
def _verify_recaptcha_with_score(token, secret):
try:
response = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": secret, "response": token},
timeout=10,
)
data = response.json()
except requests.RequestException:
return False, "Не удалось проверить captcha", 0.0
if data.get("success"):
return True, None, float(data.get("score") or 1.0)
return False, "Captcha не пройдена", 0.0