Skip to content

Commit

Permalink
Upgraded django-recaptcha to 4.0.0.
Browse files Browse the repository at this point in the history
Co-authored-by: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com>
  • Loading branch information
2 people authored and felixxm committed Feb 23, 2024
1 parent 8478173 commit 973c394
Show file tree
Hide file tree
Showing 10 changed files with 46 additions and 19 deletions.
4 changes: 2 additions & 2 deletions contact/forms.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import logging

import django
from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV3
from django import forms
from django.conf import settings
from django.contrib.sites.models import Site
from django.utils.encoding import force_bytes
from django_contact_form.forms import ContactForm
from django_recaptcha.fields import ReCaptchaField
from django_recaptcha.widgets import ReCaptchaV3
from pykismet3 import Akismet, AkismetServerError

logger = logging.getLogger(__name__)
Expand Down
2 changes: 1 addition & 1 deletion djangoproject/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"svntogit",
"tracdb",
"fundraising",
"captcha",
"django_recaptcha",
"registration",
"django_hosts",
"sorl.thumbnail",
Expand Down
2 changes: 1 addition & 1 deletion djangoproject/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@
"djangoproject.middleware.CORSMiddleware",
)

SILENCED_SYSTEM_CHECKS = ["captcha.recaptcha_test_key_error"]
SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"]
2 changes: 1 addition & 1 deletion djangoproject/settings/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
SECRET_KEY = os.environ.get("SECRET_KEY")

SILENCED_SYSTEM_CHECKS = SILENCED_SYSTEM_CHECKS + [
"captcha.recaptcha_test_key_error" # Default test keys for development.
"django_recaptcha.recaptcha_test_key_error" # Default test keys for development.
]

ALLOWED_HOSTS = [".localhost", "127.0.0.1", "www.127.0.0.1"]
Expand Down
2 changes: 1 addition & 1 deletion djangoproject/settings/prod.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
)

# RECAPTCHA KEYS
# Defaults will trigger 'captcha.recaptcha_test_key_error' system check
# Defaults will trigger 'django_recaptcha.recaptcha_test_key_error' system check
if "recaptcha_public_key" in SECRETS:
RECAPTCHA_PUBLIC_KEY = SECRETS.get("recaptcha_public_key")
RECAPTCHA_PRIVATE_KEY = SECRETS.get("recaptcha_private_key")
Expand Down
24 changes: 20 additions & 4 deletions djangoproject/static/js/mod/stripe-donation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ define([
'stripe'
], function($) {
var $donationForm = $('.stripe-donation');
var $submitButton = $donationForm.find('.cta');

$donationForm.on('submit', function (e) {
e.preventDefault();
function postToStripe(recaptchaToken) {
var interval = $donationForm.find('[name=interval]').val();
var amount = $donationForm.find('[name=amount]').val();
var recaptchaToken = document.getElementById('id_captcha').value;
var csrfToken = $donationForm.find('[name=csrfmiddlewaretoken]').val();
var data = {
'interval': interval,
Expand Down Expand Up @@ -38,6 +35,25 @@ define([
}
}
})
};

// django-recaptcha==4.0.0 adds a `submit` event listener to the form that
// ends up calling form.submit(), therefore bypassing our own event listener.
// As a workaround, we remove their event listener and replace it with our own.
if (window.recaptchaFormSubmit !== undefined) {
$donationForm[0].removeEventListener("submit", window.recaptchaFormSubmit);
}
$donationForm.on('submit', function (e) {
e.preventDefault();
let captcha_input = document.getElementById("id_captcha"),
public_key = captcha_input.getAttribute('data-sitekey');
// Validate token on form submit.
// NOTE: the `action` key must match the one defined on the widget.
grecaptcha.execute(public_key, {action: 'form'}).then(function(token) {
captcha_input.value = token;
console.log("reCAPTCHA validated. Posting to stripe...");
postToStripe(token);
});
});

});
9 changes: 5 additions & 4 deletions fundraising/forms.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import stripe
from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV3
from django import forms
from django.utils.safestring import mark_safe
from django_recaptcha.fields import ReCaptchaField
from django_recaptcha.widgets import ReCaptchaV3

from .models import INTERVAL_CHOICES, LEADERSHIP_LEVEL_AMOUNT, DjangoHero, Donation

Expand Down Expand Up @@ -134,7 +134,7 @@ class DonateForm(forms.Form):

amount = forms.ChoiceField(choices=AMOUNT_CHOICES)
interval = forms.ChoiceField(choices=INTERVAL_CHOICES)
captcha = ReCaptchaField(widget=ReCaptchaV3)
captcha = ReCaptchaField(widget=ReCaptchaV3(action="form"))


class DonationForm(forms.ModelForm):
Expand Down Expand Up @@ -176,7 +176,8 @@ class PaymentForm(forms.Form):
`amount` can be any integer, so a ChoiceField is not appropriate.
"""

captcha = ReCaptchaField(widget=ReCaptchaV3)
# NOTE: `action` needs to match the one used in stripe-donation.js
captcha = ReCaptchaField(widget=ReCaptchaV3(action="form"))
amount = forms.IntegerField(
required=True,
min_value=1, # Minimum payment from Stripe API
Expand Down
13 changes: 10 additions & 3 deletions fundraising/tests/test_forms.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
from unittest.mock import patch

from django.test import TestCase
from django_recaptcha.client import RecaptchaResponse

from ..forms import PaymentForm


class TestPaymentForm(TestCase):
def test_basics(self):
@patch("django_recaptcha.fields.client.submit")
def test_basics(self, client_submit):
client_submit.return_value = RecaptchaResponse(is_valid=True, action="form")
form = PaymentForm(
data={
"amount": 100,
"interval": "onetime",
"captcha": "TESTING",
}
)
self.assertTrue(form.is_valid())
self.assertTrue(form.is_valid(), form.errors)

def test_max_value_validation(self):
@patch("django_recaptcha.fields.client.submit")
def test_max_value_validation(self, client_submit):
client_submit.return_value = RecaptchaResponse(is_valid=True, action="form")
"""
Reject unrealistic values greater than $1,000,000.
"""
Expand Down
5 changes: 4 additions & 1 deletion fundraising/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.test.utils import override_settings
from django.urls import reverse
from django_hosts.resolvers import reverse as django_hosts_reverse
from django_recaptcha.client import RecaptchaResponse

from ..models import DjangoHero, Donation

Expand Down Expand Up @@ -59,8 +60,10 @@ def test_submitting_donation_form_invalid_amount(self):
self.assertFalse(content["success"])

@patch("stripe.checkout.Session.create")
def test_submitting_donation_form_valid(self, session_create):
@patch("django_recaptcha.fields.client.submit")
def test_submitting_donation_form_valid(self, client_submit, session_create):
session_create.return_value = {"id": "TEST_ID"}
client_submit.return_value = RecaptchaResponse(is_valid=True, action="form")
response = self.client.post(
reverse("fundraising:donation-session"),
{
Expand Down
2 changes: 1 addition & 1 deletion requirements/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ django-hosts==5.1
django-money==3.1
django-push==1.1
django-read-only==1.12.0
django-recaptcha==3.0.0
django-recaptcha==4.0.0
django-registration-redux==2.13
Django==3.2.23
docutils==0.17.1
Expand Down

0 comments on commit 973c394

Please sign in to comment.