Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions captcha/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from urllib.parse import urlencode
from urllib.request import ProxyHandler, Request, build_opener

import requests
from django.conf import settings

from captcha.constants import DEFAULT_RECAPTCHA_DOMAIN
Expand Down Expand Up @@ -68,3 +69,26 @@ def submit(recaptcha_response, private_key, remoteip):
error_codes=data.pop("error-codes", None),
extra_data=data,
)


def submit_enterprise(recaptcha_response, site_key, google_server_api_key,
google_project_id, expected_action):
# v1beta1 allows auth by API key
url = f"https://recaptchaenterprise.googleapis.com/v1beta1/projects/{google_project_id}/assessments?key={google_server_api_key}"
recaptcha_payload = {
"event": {
"token": recaptcha_response,
# need to let the client pass this along
"siteKey": site_key,
"expectedAction": expected_action,
}
}

response = requests.post(url, json=recaptcha_payload)
import pdb; pdb.set_trace();
data = response.content
return RecaptchaResponse(
is_valid=data["valid"],
error_codes=data["invalidReason"],
extra_data=data,
)
35 changes: 27 additions & 8 deletions captcha/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

from captcha import client
from captcha.constants import TEST_PRIVATE_KEY, TEST_PUBLIC_KEY
from captcha.widgets import ReCaptchaBase, ReCaptchaV2Checkbox
from captcha.widgets import ReCaptchaBase, ReCaptchaV2Checkbox, \
RecaptchaEnterprise

logger = logging.getLogger(__name__)

Expand All @@ -21,7 +22,10 @@ class ReCaptchaField(forms.CharField):
"captcha_error": _("Error verifying reCAPTCHA, please try again."),
}

def __init__(self, public_key=None, private_key=None, *args, **kwargs):
def __init__(self, public_key=None, private_key=None,
enterprise=None, google_server_api_key=None,
google_project_id=None, expected_action=None,
*args, **kwargs):
"""
ReCaptchaField can accepts attributes which is a dictionary of
attributes to be passed to the ReCaptcha widget class. The widget will
Expand All @@ -48,6 +52,14 @@ def __init__(self, public_key=None, private_key=None, *args, **kwargs):
settings, "RECAPTCHA_PUBLIC_KEY", TEST_PUBLIC_KEY
)

if enterprise:
self.enterprise = True
self.google_server_api_key = google_server_api_key
self.google_project_id = google_project_id
self.expected_action = expected_action
else:
self.enterprise = False

# Update widget attrs with data-sitekey.
self.widget.attrs["data-sitekey"] = self.public_key

Expand All @@ -66,13 +78,20 @@ def validate(self, value):
super().validate(value)

try:
check_captcha = client.submit(
recaptcha_response=value,
private_key=self.private_key,
remoteip=self.get_remote_ip(),
)
if self.enterprise:
check_captcha = client.submit_enterprise(value,
self.public_key,
self.google_server_api_key,
self.google_project_id,
self.expected_action)
else:
check_captcha = client.submit(
recaptcha_response=value,
private_key=self.private_key,
remoteip=self.get_remote_ip(),
)

except HTTPError: # Catch timeouts, etc
except HTTPError as e: # Catch timeouts, etc
raise ValidationError(
self.error_messages["captcha_error"], code="captcha_error"
)
Expand Down
22 changes: 22 additions & 0 deletions captcha/templates/captcha/includes/js_enterprise.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{# The provided implementation caters for only one reCAPTCHA on a page. Override this template and its logic as needed. #}
<script src="https://{{ recaptcha_domain }}/recaptcha/enterprise.js?render={{ public_key }}{% if api_params %}&{{ api_params }}{% endif %}"></script>
<script type="text/javascript">
grecaptcha.enterprise.ready(function() {
recaptcha_refresh_token_enterprise();
});

/*
We declare this function so that it is possible for us to refresh the token
in a page without reloading the whole page. This is useful for example
when we have a ReCAPTCHA protected form in a modal dialog that does things
with AJAX.
*/
function recaptcha_refresh_token_enterprise() {
grecaptcha.enterprise.execute('{{ public_key }}', {action: 'form'})
.then(function(token) {
console.log("reCAPTCHA validated for 'data-widget-uuid=\"{{ widget_uuid }}\"'. Setting input value...")
var element = document.querySelector('.g-recaptcha[data-widget-uuid="{{ widget_uuid }}"]');
element.value = token;
});
}
</script>
14 changes: 12 additions & 2 deletions captcha/templates/captcha/includes/js_v3.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
<script src="https://{{ recaptcha_domain }}/recaptcha/api.js?render={{ public_key }}{% if api_params %}&{{ api_params }}{% endif %}"></script>
<script type="text/javascript">
grecaptcha.ready(function() {
grecaptcha.execute('{{ public_key }}', {action: 'form'})
recaptcha_refresh_token();
});

/*
We declare this function so that it is possible for us to refresh the token
in a page without reloading the whole page. This is useful for example
when we have a ReCAPTCHA protected form in a modal dialog that does things
with AJAX.
*/
function recaptcha_refresh_token() {
grecaptcha.execute('{{ public_key }}', {action: 'form'})
.then(function(token) {
console.log("reCAPTCHA validated for 'data-widget-uuid=\"{{ widget_uuid }}\"'. Setting input value...")
var element = document.querySelector('.g-recaptcha[data-widget-uuid="{{ widget_uuid }}"]');
element.value = token;
});
});
}
</script>
6 changes: 6 additions & 0 deletions captcha/templates/captcha/widget_enterprise.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% include "captcha/includes/js_enterprise.html" %}
<input class="g-recaptcha"
type="hidden"
name="{{ widget.name }}"
{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
>
4 changes: 4 additions & 0 deletions captcha/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,7 @@ def build_attrs(self, base_attrs, extra_attrs=None):

def value_from_datadict(self, data, files, name):
return data.get(name)


class RecaptchaEnterprise(ReCaptchaV3):
template_name = "captcha/widget_enterprise.html"
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
tox
tox-gh-actions
coveralls
requests==2.26.0