From 4ce06f75a91d24ec049a94a8f384532f4113c951 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 30 Oct 2025 15:38:40 +0100 Subject: [PATCH] fix --- rowers/forms.py | 19 ++++++++++++++++++- rowers/templates/registration_form.html | 2 ++ rowers/tests/test_newusers.py | 21 ++++++++++++++++----- rowers/tests/testdata/testdata.tcx.gz | Bin 3989 -> 3989 bytes rowers/views/paymentviews.py | 10 +++++++--- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/rowers/forms.py b/rowers/forms.py index ad865dd6..e00c49b7 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1066,7 +1066,8 @@ class RegistrationFormSex(RegistrationFormUniqueEmail): month=4, day=15)) - url = HoneypotField(label="URL") + hp_field = HoneypotField(label="URL") + timestamp = forms.CharField(widget=forms.HiddenInput(), required=False) def clean_birthdate(self): dob = self.cleaned_data['birthdate'] @@ -1087,6 +1088,22 @@ class RegistrationFormSex(RegistrationFormUniqueEmail): adaptiveclass = forms.ChoiceField(label='Adaptive Classification', choices=adaptivecategories, initial='None', required=False) + captcha = ReCaptchaField(widget=ReCaptchaV3( + attrs={ + 'required_score': 0.85, + })) + + + def clean(self): + cleaned_data = super().clean() + timestamp = cleaned_data.get('timestamp') + if timestamp: + submission_time = timezone.now() + form_render_time = timezone.datetime.fromtimestamp(float(timestamp), tz=timezone.get_current_timezone()) + if (submission_time - form_render_time).seconds < 3: + raise forms.ValidationError("Form submitted too quickly. Are you a bot?") + return cleaned_data + class PowerIntervalUpdateForm(forms.Form): selectorchoices = ( diff --git a/rowers/templates/registration_form.html b/rowers/templates/registration_form.html index 29328f81..1c3cce24 100644 --- a/rowers/templates/registration_form.html +++ b/rowers/templates/registration_form.html @@ -31,6 +31,8 @@ {{ form.as_table }}
+ +

Terms of Service

diff --git a/rowers/tests/test_newusers.py b/rowers/tests/test_newusers.py index 138643cb..a47e7b8c 100644 --- a/rowers/tests/test_newusers.py +++ b/rowers/tests/test_newusers.py @@ -5,7 +5,8 @@ from __future__ import unicode_literals #from __future__ import print_function from .statements import * - +from django_recaptcha.client import RecaptchaResponse +from django_recaptcha.fields import ReCaptchaField nu = datetime.datetime.now() #@pytest.mark.django_db @@ -22,7 +23,11 @@ class NewUserRegistrationTest(TestCase): pass @patch('rowers.dataprep.workout_summary_to_df',side_effect=mock_workout_summaries) - def test_newuser(self,mock_workout_summaries): + @patch("django_recaptcha.fields.client.submit") + def test_newuser(self,mock_submit, mock_workout_summaries): + mock_response = RecaptchaResponse(is_valid=True, extra_data={'score': 0.95}) + mock_submit.return_value = mock_response + form_data = { 'first_name':'Jan', 'last_name':'Roeiert', @@ -30,16 +35,21 @@ class NewUserRegistrationTest(TestCase): 'username':'janderoeiert', 'password1':'Aapindewei2', 'password2':'Aapindewei2', - 'url': '', + 'hp_field': '', 'tos':True, 'weightcategory':'hwt', 'adaptiveclass': 'None', 'sex':'male', + 'captcha': 'PASSED', + 'g-recaptcha-response': 'PASSED', 'next':'/rowers/list-workouts', 'birthdate':datetime.datetime(year=1970,month=4,day=2) } form = RegistrationFormSex(form_data) + if not form.is_valid(): + print(form.errors) + self.assertTrue(form.is_valid()) response = self.c.post('/rowers/register/', form_data, follow=True) @@ -118,7 +128,7 @@ class NewUserRegistrationTest(TestCase): 'password1':'aapindewei2', 'password2':'aapindewei2', 'tos':True, - 'url': '', + 'hp_field': '', 'weightcategory':'hwt', 'adaptiveclass': 'None', 'sex':'male', @@ -126,6 +136,7 @@ class NewUserRegistrationTest(TestCase): 'birthdate':datetime.datetime(year=1970,month=4,day=2) } + form = RegistrationFormSex(form_data) self.assertFalse(form.is_valid()) @@ -138,7 +149,7 @@ class NewUserRegistrationTest(TestCase): 'username':'janderoeiert', 'password1':'Aapindewei2', 'password2':'Aapindewei2', - 'url': 'http://example.com', + 'hp_field': 'http://example.com', 'tos':True, 'weightcategory':'hwt', 'adaptiveclass': 'None', diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index 322910d9b180d6de02d43d8fb434e6696a3d4ab7..b442ff525ba367320531a670b39359a2b1f9f244 100644 GIT binary patch delta 17 YcmbO#KUJPXzMF$1v5+})BS$Yk04oCoz5oCK delta 17 YcmbO#KUJPXzMF%Cr{#CXMvh*704}Qqa{vGU diff --git a/rowers/views/paymentviews.py b/rowers/views/paymentviews.py index fc04b4bc..54808712 100644 --- a/rowers/views/paymentviews.py +++ b/rowers/views/paymentviews.py @@ -5,6 +5,7 @@ from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.contrib.auth.backends import ModelBackend from rowers.views.statements import * from django.core.mail import EmailMessage +from django_ratelimit.decorators import ratelimit from rowers import credits @@ -888,6 +889,7 @@ def useractivate(request, uidb64, token): # pragma: no cover # User registration +@ratelimit(key='ip', rate='5/h', method='POST') def rower_register_view(request): nextpage = request.GET.get('next', '/rowers/list-workouts/') @@ -896,7 +898,7 @@ def rower_register_view(request): if request.method == 'POST': # Check if honeypot was triggered (optional logging) - honeypot_value = request.POST.get('url', '') + honeypot_value = request.POST.get('hp_field', '') if honeypot_value: # bot user, do not register messages.error(request, "Registration failed. Please try again.") @@ -985,13 +987,15 @@ def rower_register_view(request): return render(request, "registration_form.html", {'form': form, - 'next': nextpage, }) + 'next': nextpage, + 'timestamp': timezone.now().timestamp(),}) else: form = RegistrationFormSex() return render(request, "registration_form.html", {'form': form, - 'next': nextpage, }) + 'next': nextpage, + 'timestamp': timezone.now().timestamp(),}) # User registration