fix: updated captcha api to use enterprise assessment (#37079)

* fix: updated captcha api to use enterprise assessment
This commit is contained in:
Ahtisham Shahid
2025-08-04 02:27:12 +05:00
committed by GitHub
parent 32abbc2763
commit c2a86534e6
6 changed files with 82 additions and 33 deletions

View File

@@ -25,7 +25,7 @@ def is_forum_daily_digest_enabled():
# .. toggle_name: discussion.enable_captcha
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_default: False
# .. toggle_description: Waffle flag to enable account level preferences for notifications
# .. toggle_description: When the flag is ON, users will be able to see captcha for discussion
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2025-07-12
# .. toggle_target_removal_date: 2025-07-29

View File

@@ -130,7 +130,7 @@ from .utils import (
get_usernames_from_search_string,
set_attribute,
is_posting_allowed,
can_user_notify_all_learners, is_captcha_enabled
can_user_notify_all_learners, is_captcha_enabled, get_captcha_site_key_by_platform
)
User = get_user_model()
@@ -383,7 +383,7 @@ def get_course(request, course_key, check_tab=True):
),
'captcha_settings': {
'enabled': is_captcha_enabled(course_key),
'site_key': settings.RECAPTCHA_SITE_KEY,
'site_key': get_captcha_site_key_by_platform('web'),
},
"is_email_verified": request.user.is_active,
"only_verified_users_can_post": ONLY_VERIFIED_USERS_CAN_POST.is_enabled(course_key),

View File

@@ -218,7 +218,7 @@ class GetCourseTest(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase)
'is_notify_all_learners_enabled': False,
'captcha_settings': {
'enabled': False,
'site_key': '',
'site_key': None,
},
"is_email_verified": True,
"only_verified_users_can_post": False,

View File

@@ -570,7 +570,7 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
'is_notify_all_learners_enabled': False,
'captcha_settings': {
'enabled': False,
'site_key': '',
'site_key': None,
},
"is_email_verified": True,
"only_verified_users_can_post": False,

View File

@@ -6,6 +6,7 @@ from datetime import datetime
from typing import Dict, List
import requests
from crum import get_current_request
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core.paginator import Paginator
@@ -413,26 +414,6 @@ def can_user_notify_all_learners(user_roles, is_course_staff, is_course_admin):
return is_staff_or_instructor
def verify_recaptcha_token(token):
"""
Helper function to verify reCAPTCHA token
"""
verify_url = settings.RECAPTCHA_VERIFY_URL
verify_data = {
'secret': settings.RECAPTCHA_PRIVATE_KEY,
'response': token,
}
try:
response = requests.post(verify_url, data=verify_data, timeout=10)
result = response.json()
log.info("reCAPTCHA verification result: %s", result)
return result.get('success', False)
except Exception as e: # pylint: disable=broad-except
log.error("Error verifying reCAPTCHA token: %s", e)
return False
def is_captcha_enabled(course_id) -> bool:
"""
Check if reCAPTCHA is enabled for discussion posts in the given course.
@@ -463,3 +444,55 @@ def is_only_student(course_key, user) -> bool:
is_user_admin = user.is_staff
user_roles = get_user_role_names(user, course_key)
return user_roles == {FORUM_ROLE_STUDENT} and not (is_course_staff_or_admin or is_user_admin)
def verify_recaptcha_token(token: str) -> bool:
"""
Assess the reCAPTCHA token using Google reCAPTCHA Enterprise API.
Logs success or error and returns True if an error occurs, along with logging the error.
"""
try:
site_key = get_captcha_site_key_by_platform(get_platform_from_request())
url = (f"https://recaptchaenterprise.googleapis.com/v1/projects/{settings.RECAPTCHA_PROJECT_ID}/assessments"
f"?key={settings.RECAPTCHA_PRIVATE_KEY}")
data = {
"event": {
"token": token,
"siteKey": site_key,
}
}
response = requests.post(url, json=data, timeout=10).json()
if response.get('tokenProperties', {}).get('valid'):
logging.info("reCAPTCHA token assessment successful. Token is valid.")
return True
elif response.get('error'):
logging.error(f"reCAPTCHA token assessment failed: {response['error']}.")
return True
else:
logging.error(f"reCAPTCHA token assessment failed: Invalid token.{response}.")
return False
except requests.exceptions.RequestException as e:
logging.error(f"Network or API error during reCAPTCHA assessment: {e}")
return True
except KeyError as e:
logging.error(f"Unexpected response format from reCAPTCHA API. Missing key: {e}. Full response: {response}")
return True
except Exception as e: # lint-amnesty, pylint: disable=broad-except
logging.error(f"An unexpected error occurred during reCAPTCHA assessment: {e}", exc_info=True)
return True
def get_platform_from_request():
"""
get Mobile-Platform-Identifier header value from request
"""
return get_current_request().headers.get('Mobile-Platform-Identifier', 'web')
def get_captcha_site_key_by_platform(platform: str) -> str | None:
"""
Get reCAPTCHA site key based on the platform.
"""
return settings.RECAPTCHA_SITE_KEYS.get(platform, None)

View File

@@ -5001,12 +5001,28 @@ LMS_COMM_DEFAULT_FROM_EMAIL = "no-reply@example.com"
# .. setting_description: Add recaptcha private key to use captcha feature in discussion app.
RECAPTCHA_PRIVATE_KEY = ""
# .. setting_name: RECAPTCHA_VERIFY_URL
# .. setting_default: empty string
# .. setting_description: Add recaptcha verification api url to verify capthca tokens.
RECAPTCHA_VERIFY_URL = ""
# .. setting_name: RECAPTCHA_SITE_KEYS
# .. setting_default: empty dictionary
# .. setting_description: Add recaptcha site keys to use captcha feature in discussion app.
# .. setting_warning: This setting is used to configure the reCAPTCHA keys for web,
# iOS, and Android platforms.
# The keys are expected to be in the format:
# {
# 'web': 'your-web-site-key',
# 'ios': 'your-ios-site-key',
# 'android': 'your-android-site-key',
# }
RECAPTCHA_SITE_KEYS = {
'web': None,
'ios': None,
'android': None,
}
# .. setting_name: RECAPTCHA_SITE_KEY
# .. setting_default: empty string
# .. setting_description: Add recaptcha site key to use captcha feature in discussion MFE.
RECAPTCHA_SITE_KEY = ""
# .. setting_name: RECAPTCHA_PROJECT_ID
# .. setting_default: None
# .. setting_description: Add recaptcha project id to use captcha feature in discussion app.
# .. setting_warning: This setting is used to configure the reCAPTCHA project ID for the discussion app.
# The project ID is used to identify the reCAPTCHA project in the Google Cloud Console
# and is required for the reCAPTCHA service to function correctly.
# The project ID should be obtained from the Google Cloud Console when creating a reCAPTCHA
RECAPTCHA_PROJECT_ID = None