Skip to content

Commit 790c66a

Browse files
authored
Add recaptcha to registration form (#13232)
* Add recaptcha to registration form * Actually use post_body * Fix new test * Update translations
1 parent 14f3b11 commit 790c66a

File tree

12 files changed

+646
-68
lines changed

12 files changed

+646
-68
lines changed

dev/environment

+4
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ TWOFACTORMANDATE_AVAILABLE=true
6262
TWOFACTORMANDATE_ENABLED=true
6363
OIDC_ENABLED=true
6464
OIDC_AUDIENCE=pypi
65+
66+
# Default to the reCAPTCHA testing keys from https://developers.google.com/recaptcha/docs/faq
67+
RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
68+
RECAPTCHA_SECRET_KEY=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe

tests/unit/accounts/test_forms.py

+81-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import pytest
1717
import wtforms
1818

19+
from warehouse import recaptcha
1920
from warehouse.accounts import forms
2021
from warehouse.accounts.interfaces import (
2122
BurnedRecoveryCode,
@@ -344,19 +345,26 @@ def test_validate_password_notok_ip_banned(self, db_session):
344345
class TestRegistrationForm:
345346
def test_create(self):
346347
user_service = pretend.stub()
348+
recaptcha_service = pretend.stub(enabled=True)
347349
breach_service = pretend.stub()
348350

349351
form = forms.RegistrationForm(
350-
data={}, user_service=user_service, breach_service=breach_service
352+
data={},
353+
user_service=user_service,
354+
recaptcha_service=recaptcha_service,
355+
breach_service=breach_service,
351356
)
357+
352358
assert form.user_service is user_service
359+
assert form.recaptcha_service is recaptcha_service
353360

354361
def test_password_confirm_required_error(self):
355362
form = forms.RegistrationForm(
356363
data={"password_confirm": ""},
357364
user_service=pretend.stub(
358365
find_userid_by_email=pretend.call_recorder(lambda _: pretend.stub())
359366
),
367+
recaptcha_service=pretend.stub(enabled=True),
360368
breach_service=pretend.stub(check_password=lambda pw: False),
361369
)
362370

@@ -370,6 +378,7 @@ def test_passwords_mismatch_error(self, pyramid_config):
370378
form = forms.RegistrationForm(
371379
data={"new_password": "password", "password_confirm": "mismatch"},
372380
user_service=user_service,
381+
recaptcha_service=pretend.stub(enabled=True),
373382
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
374383
)
375384

@@ -389,6 +398,7 @@ def test_passwords_match_success(self):
389398
"password_confirm": "MyStr0ng!shPassword",
390399
},
391400
user_service=user_service,
401+
recaptcha_service=pretend.stub(enabled=True),
392402
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
393403
)
394404

@@ -402,6 +412,7 @@ def test_email_required_error(self):
402412
user_service=pretend.stub(
403413
find_userid_by_email=pretend.call_recorder(lambda _: pretend.stub())
404414
),
415+
recaptcha_service=pretend.stub(enabled=True),
405416
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
406417
)
407418

@@ -415,6 +426,7 @@ def test_invalid_email_error(self, pyramid_config, email):
415426
user_service=pretend.stub(
416427
find_userid_by_email=pretend.call_recorder(lambda _: None)
417428
),
429+
recaptcha_service=pretend.stub(enabled=True),
418430
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
419431
)
420432

@@ -429,6 +441,7 @@ def test_exotic_email_success(self):
429441
user_service=pretend.stub(
430442
find_userid_by_email=pretend.call_recorder(lambda _: None)
431443
),
444+
recaptcha_service=pretend.stub(enabled=True),
432445
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
433446
)
434447

@@ -441,6 +454,7 @@ def test_email_exists_error(self, pyramid_config):
441454
user_service=pretend.stub(
442455
find_userid_by_email=pretend.call_recorder(lambda _: pretend.stub())
443456
),
457+
recaptcha_service=pretend.stub(enabled=True),
444458
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
445459
)
446460

@@ -457,6 +471,7 @@ def test_prohibited_email_error(self, pyramid_config):
457471
user_service=pretend.stub(
458472
find_userid_by_email=pretend.call_recorder(lambda _: None)
459473
),
474+
recaptcha_service=pretend.stub(enabled=True),
460475
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
461476
)
462477

@@ -467,13 +482,58 @@ def test_prohibited_email_error(self, pyramid_config):
467482
"different email."
468483
)
469484

485+
def test_recaptcha_disabled(self):
486+
form = forms.RegistrationForm(
487+
data={"g_recpatcha_response": ""},
488+
user_service=pretend.stub(),
489+
recaptcha_service=pretend.stub(
490+
enabled=False,
491+
verify_response=pretend.call_recorder(lambda _: None),
492+
),
493+
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
494+
)
495+
assert not form.validate()
496+
# there shouldn't be any errors for the recaptcha field if it's
497+
# disabled
498+
assert not form.g_recaptcha_response.errors
499+
500+
def test_recaptcha_required_error(self):
501+
form = forms.RegistrationForm(
502+
data={"g_recaptcha_response": ""},
503+
user_service=pretend.stub(),
504+
recaptcha_service=pretend.stub(
505+
enabled=True,
506+
verify_response=pretend.call_recorder(lambda _: None),
507+
),
508+
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
509+
)
510+
assert not form.validate()
511+
assert form.g_recaptcha_response.errors.pop() == "Recaptcha error."
512+
513+
def test_recaptcha_error(self):
514+
form = forms.RegistrationForm(
515+
data={"g_recaptcha_response": "asd"},
516+
user_service=pretend.stub(),
517+
recaptcha_service=pretend.stub(
518+
verify_response=pretend.raiser(recaptcha.RecaptchaError),
519+
enabled=True,
520+
),
521+
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
522+
)
523+
assert not form.validate()
524+
assert form.g_recaptcha_response.errors.pop() == "Recaptcha error."
525+
470526
def test_username_exists(self, pyramid_config):
471527
form = forms.RegistrationForm(
472528
data={"username": "foo"},
473529
user_service=pretend.stub(
474530
find_userid=pretend.call_recorder(lambda name: 1),
475531
username_is_prohibited=lambda a: False,
476532
),
533+
recaptcha_service=pretend.stub(
534+
enabled=False,
535+
verify_response=pretend.call_recorder(lambda _: None),
536+
),
477537
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
478538
)
479539
assert not form.validate()
@@ -489,6 +549,10 @@ def test_username_prohibted(self, pyramid_config):
489549
user_service=pretend.stub(
490550
username_is_prohibited=lambda a: True,
491551
),
552+
recaptcha_service=pretend.stub(
553+
enabled=False,
554+
verify_response=pretend.call_recorder(lambda _: None),
555+
),
492556
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
493557
)
494558
assert not form.validate()
@@ -506,6 +570,10 @@ def test_username_is_valid(self, username, pyramid_config):
506570
find_userid=pretend.call_recorder(lambda _: None),
507571
username_is_prohibited=lambda a: False,
508572
),
573+
recaptcha_service=pretend.stub(
574+
enabled=False,
575+
verify_response=pretend.call_recorder(lambda _: None),
576+
),
509577
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
510578
)
511579
assert not form.validate()
@@ -527,6 +595,10 @@ def test_password_strength(self):
527595
form = forms.RegistrationForm(
528596
data={"new_password": pwd, "password_confirm": pwd},
529597
user_service=pretend.stub(),
598+
recaptcha_service=pretend.stub(
599+
enabled=False,
600+
verify_response=pretend.call_recorder(lambda _: None),
601+
),
530602
breach_service=pretend.stub(check_password=lambda pw, tags=None: False),
531603
)
532604
form.validate()
@@ -538,6 +610,10 @@ def test_password_breached(self):
538610
user_service=pretend.stub(
539611
find_userid=pretend.call_recorder(lambda _: None)
540612
),
613+
recaptcha_service=pretend.stub(
614+
enabled=False,
615+
verify_response=pretend.call_recorder(lambda _: None),
616+
),
541617
breach_service=pretend.stub(
542618
check_password=lambda pw, tags=None: True,
543619
failure_message=(
@@ -558,6 +634,10 @@ def test_name_too_long(self, pyramid_config):
558634
user_service=pretend.stub(
559635
find_userid=pretend.call_recorder(lambda _: None)
560636
),
637+
recaptcha_service=pretend.stub(
638+
enabled=False,
639+
verify_response=pretend.call_recorder(lambda _: None),
640+
),
561641
breach_service=pretend.stub(check_password=lambda pw, tags=None: True),
562642
)
563643
assert not form.validate()

tests/unit/accounts/test_views.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1347,8 +1347,10 @@ def test_register_redirect(self, db_request, monkeypatch):
13471347
lambda *args: None
13481348
)
13491349
db_request.session.record_password_timestamp = lambda ts: None
1350-
db_request.find_service = pretend.call_recorder(
1351-
lambda svc, name=None, context=None: {
1350+
1351+
def _find_service(service=None, name=None, context=None):
1352+
key = service or name
1353+
return {
13521354
IUserService: pretend.stub(
13531355
username_is_prohibited=lambda a: False,
13541356
find_userid=pretend.call_recorder(lambda _: None),
@@ -1364,8 +1366,13 @@ def test_register_redirect(self, db_request, monkeypatch):
13641366
check_password=lambda pw, tags=None: False,
13651367
),
13661368
IRateLimiter: pretend.stub(hit=lambda user_id: None),
1367-
}.get(svc)
1368-
)
1369+
"csp": pretend.stub(merge=lambda *a, **kw: {}),
1370+
"recaptcha": pretend.stub(
1371+
csp_policy={}, enabled=True, verify_response=lambda a: True
1372+
),
1373+
}[key]
1374+
1375+
db_request.find_service = pretend.call_recorder(_find_service)
13691376
db_request.route_path = pretend.call_recorder(lambda name: "/")
13701377
db_request.POST.update(
13711378
{
@@ -1374,6 +1381,7 @@ def test_register_redirect(self, db_request, monkeypatch):
13741381
"password_confirm": "MyStr0ng!shP455w0rd",
13751382
"email": "foo@bar.com",
13761383
"full_name": "full_name",
1384+
"g_recaptcha_response": "captchavalue",
13771385
}
13781386
)
13791387

tests/unit/test_config.py

+1
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ def __init__(self):
370370
pretend.call(".sentry"),
371371
pretend.call(".csp"),
372372
pretend.call(".referrer_policy"),
373+
pretend.call(".recaptcha"),
373374
pretend.call(".http"),
374375
]
375376
+ [pretend.call(x) for x in [configurator_settings.get("warehouse.theme")] if x]

0 commit comments

Comments
 (0)