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