Merge lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-web into lp:canonical-identity-provider/release

Proposed by Maximiliano Bertacchini
Status: Merged
Merged at revision: 1668
Proposed branch: lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-web
Merge into: lp:canonical-identity-provider/release
Prerequisite: lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-api-v1
Diff against target: 448 lines (+2/-258)
7 files modified
src/webui/templates/registration/_create_account_form.html (+0/-11)
src/webui/templates/widgets/recaptcha.html (+0/-43)
src/webui/tests/test_views_registration.py (+0/-51)
src/webui/tests/test_views_ui.py (+1/-85)
src/webui/views/registration.py (+0/-26)
src/webui/views/ui.py (+1/-35)
src/webui/views/utils.py (+0/-7)
To merge this branch: bzr merge lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-web
Reviewer Review Type Date Requested Status
Matias Bordese (community) Approve
Review via email: mp+356877@code.launchpad.net

Commit message

Drop captcha from account registration via web.

Description of the change

Account registration captcha has been disabled for a long time (years?). And the implementation depends on reCaptcha v1 which is dead since March 2018. So, let's just drop all captcha bits from there.

To post a comment you must log in.
Revision history for this message
Matias Bordese (matiasb) wrote :

LGTM

review: Approve
Revision history for this message
Otto Co-Pilot (otto-copilot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/webui/templates/registration/_create_account_form.html'
2--- src/webui/templates/registration/_create_account_form.html 2018-07-04 17:51:43 +0000
3+++ src/webui/templates/registration/_create_account_form.html 2018-10-22 16:30:41 +0000
4@@ -50,17 +50,6 @@
5
6 {% include "widgets/passwords.html" with fields=create_form %}
7
8- {% if captcha_required %}
9- <div class="captcha" id="captcha">
10- {% if captcha_error_message %}
11- <span class="error">
12- {{ captcha_error_message }}
13- </span>
14- {% endif %}
15- {% include "widgets/recaptcha.html" %}
16- </div>
17- {% endif %}
18-
19 <div class="input-row{% if create_form.accept_tos.errors %} haserrors{% endif %} accept-tos-input">
20
21 {% if create_form.accept_tos.errors %}
22
23=== removed file 'src/webui/templates/widgets/recaptcha.html'
24--- src/webui/templates/widgets/recaptcha.html 2014-12-11 17:44:29 +0000
25+++ src/webui/templates/widgets/recaptcha.html 1970-01-01 00:00:00 +0000
26@@ -1,43 +0,0 @@
27-{% comment %}
28-Copyright 2010 Canonical Ltd. This software is licensed under the
29-GNU Affero General Public License version 3 (see the file LICENSE).
30-{% endcomment %}
31-
32-{% load i18n %}
33-{% load static_url %}
34-<script type="text/javascript">
35- var RecaptchaOptions = {
36- theme: 'white',
37- custom_translations: {
38- visual_challenge : "{% trans "Get a visual challenge" %}",
39- audio_challenge : "{% trans "Get an audio challenge" %}",
40- refresh_btn : "{% trans "Get a new challenge" %}",
41- instructions_visual : "{% trans "Type the two words:" %}",
42- instructions_audio : "{% trans "Type what you hear:" %}",
43- help_btn : "{% trans "Help" %}",
44- play_again : "{% trans "Play sound again" %}",
45- cant_hear_this : "{% trans "Download sound as MP3" %}",
46- incorrect_try_again : "{% trans "Incorrect. Try again." %}"
47- },
48- };
49-</script>
50-<div {% if captcha_error %}class='captchaError'{% endif %}>
51-{% ifequal captcha_error "&error=no-challenge" %}
52-<p>
53-{% blocktrans with "support_form"|static_url as support_form_url %}
54-It appears that our captcha service was unable to load on this page.
55-This may be caused by a plugin on your browser.
56-Please correct this and try again. If the problem persists, please <a href="{{ support_form_url }}">contact support</a>
57-{% endblocktrans %}
58-</p>
59-{% endifequal %}
60-<script type="text/javascript" src="{{ CAPTCHA_API_URL_SECURE }}/challenge?k={{ CAPTCHA_PUBLIC_KEY }}{{ captcha_error }}">
61-</script>
62-<noscript>
63- <iframe src="{{ CAPTCHA_API_URL_SECURE }}/noscript?k={{ CAPTCHA_PUBLIC_KEY }}" height="300" width="500" frameborder="0" class="recaptcha-noscript">
64- </iframe>
65- <textarea class="recaptcha-challenge-field" name="recaptcha_challenge_field" rows="3" cols="40">
66- </textarea>
67- <input type="hidden" name="recaptcha_response_field" value="manual_challenge">
68-</noscript>
69-</div>
70
71=== modified file 'src/webui/tests/test_views_registration.py'
72--- src/webui/tests/test_views_registration.py 2018-05-28 20:15:33 +0000
73+++ src/webui/tests/test_views_registration.py 2018-10-22 16:30:41 +0000
74@@ -161,16 +161,6 @@
75 ctx = response.context_data
76 self.assertEqual(ctx['form']['email'].value(), 'test@test.com')
77
78- @switches(CAPTCHA=False)
79- def test_get_optional_captcha_switch_off(self):
80- response = self.get()
81- self.assertEqual(response.context_data['captcha_required'], False)
82-
83- @switches(CAPTCHA=True, CAPTCHA_NEW_ACCOUNT=True)
84- def test_get_optional_captcha_switch_on(self):
85- response = self.get()
86- self.assertEqual(response.context_data['captcha_required'], True)
87-
88 def test_post_required_fields(self):
89 response = self.post()
90 self.assert_form_displayed(
91@@ -213,9 +203,6 @@
92 email=self.TESTDATA['email'],
93 password=self.TESTDATA['password'],
94 displayname=self.TESTDATA['displayname'],
95- captcha_id=None,
96- captcha_solution=None,
97- create_captcha=False,
98 creation_source=WEB_CREATION_SOURCE,
99 )
100
101@@ -242,9 +229,6 @@
102 password=self.TESTDATA['password'],
103 displayname=self.TESTDATA['displayname'],
104 username=self.TESTDATA['username'],
105- captcha_id=None,
106- captcha_solution=None,
107- create_captcha=False,
108 creation_source=WEB_CREATION_SOURCE,
109 )
110 self.TESTDATA.pop('username')
111@@ -337,9 +321,6 @@
112 expected_args = dict(email=self.TESTDATA['email'],
113 password=self.TESTDATA['password'],
114 displayname=self.TESTDATA['displayname'],
115- create_captcha=False,
116- captcha_solution=None,
117- captcha_id=None,
118 creation_source='web-flow',
119 oid_token=token)
120 self.mock_api_register.assert_called_once_with(**expected_args)
121@@ -351,38 +332,6 @@
122 self.assert_form_displayed(response, email=VERIFY_EMAIL_MESSAGE)
123 self.assert_stat_calls(['error.email'])
124
125- def test_post_captcha_required(self):
126- exc = api_errors.CaptchaRequired(Mock())
127- self.mock_api_register.side_effect = exc
128- response = self.post(**self.TESTDATA)
129- self.assert_form_displayed(response)
130- self.assertEqual(response.context_data['captcha_required'], True)
131-
132- def test_post_captcha_failure(self):
133- mock_response = Mock()
134- body = {'extra': {'captcha_message': 'XXX'}}
135- exc = api_errors.CaptchaFailure(mock_response, body)
136- self.mock_api_register.side_effect = exc
137-
138- response = self.post(**self.TESTDATA)
139- self.assert_form_displayed(response)
140- self.assertEqual(response.context_data['captcha_required'], True)
141- self.assertEqual(
142- response.context_data['captcha_error'],
143- '&error=XXX')
144- self.assert_stat_calls(['error.captcha'])
145-
146- def test_post_captcha_error(self):
147- mock_response = Mock()
148- body = {}
149- exc = api_errors.CaptchaError(mock_response, body)
150- self.mock_api_register.side_effect = exc
151-
152- response = self.post(**self.TESTDATA)
153- self.assert_form_displayed(response)
154- self.assertEqual(response.context_data['captcha_required'], True)
155- self.assert_stat_calls(['error.captcha'])
156-
157
158 class RegisterTimelineTestCase(
159 SSOBaseTestCase, RegisterTestMixin, TimelineActionMixin):
160
161=== modified file 'src/webui/tests/test_views_ui.py'
162--- src/webui/tests/test_views_ui.py 2018-09-07 21:54:44 +0000
163+++ src/webui/tests/test_views_ui.py 2018-10-22 16:30:41 +0000
164@@ -10,7 +10,6 @@
165 import urllib2
166 from datetime import date
167 from functools import partial
168-from StringIO import StringIO
169 from urlparse import urlsplit
170
171 from django.conf import settings
172@@ -29,7 +28,6 @@
173 from django.test.utils import override_settings
174 from django.urls import reverse
175 from django.utils.html import escape
176-from gargoyle import gargoyle
177 from gargoyle.testutils import switches
178 from mock import Mock, patch
179 from pyquery import PyQuery
180@@ -50,11 +48,7 @@
181 OpenIDRPConfig,
182 twofactor,
183 )
184-from identityprovider.models.captcha import (
185- Captcha,
186- CaptchaResponse,
187- CaptchaV2,
188-)
189+from identityprovider.models.captcha import CaptchaV2
190 from identityprovider.models.const import (
191 AccountStatus,
192 AuthLogType,
193@@ -65,7 +59,6 @@
194 from identityprovider.tests import DEFAULT_USER_PASSWORD
195 from identityprovider.tests.test_auth import AuthLogTestCaseMixin
196 from identityprovider.tests.utils import (
197- MockHandler,
198 SSOBaseTestCase,
199 TimelineActionMixin,
200 )
201@@ -115,9 +108,6 @@
202 'passwordconfirm': 'Testing123',
203 'accept_tos': True
204 }
205- if gargoyle.is_active('CAPTCHA'):
206- data['recaptcha_challenge_field'] = 'ignored'
207- data['recaptcha_response_field'] = 'ignored'
208
209 return self.client.post(url, data, follow=follow)
210
211@@ -132,24 +122,6 @@
212 assert self.client.login(
213 username=self.data['email'], password=self.data['password'])
214
215- def request_when_captcha_fails(self, url, data):
216- class MockCaptcha(object):
217- def __init__(self, *args):
218- pass
219-
220- def verify(self, solution, ip_addr, email):
221- self.message = 'no-challenge'
222- return False
223-
224- @classmethod
225- def new(cls, env):
226- return cls()
227-
228- with patch.object(ui, 'Captcha', MockCaptcha):
229- r = self.client.post(url, data)
230-
231- return r
232-
233
234 @override_settings(LANGUAGE_CODE='es')
235 class SpanishUIViewsTestCase(BaseTestCase):
236@@ -1348,62 +1320,6 @@
237 self.assertFalse(email.is_verified)
238
239
240-@override_settings(CAPTCHA_PRIVATE_KEY='some-private-key')
241-class CaptchaVerificationTestCase(BaseTestCase):
242-
243- success_status = 302
244-
245- def setUp(self):
246- super(CaptchaVerificationTestCase, self).setUp()
247- mock_handler = MockHandler()
248- mock_handler.set_next_response(200, 'false\nno-challenge')
249- self.patch(Captcha, 'opener', new=urllib2.build_opener(mock_handler))
250-
251- p = switches(CAPTCHA=True)
252- p.patch()
253- self.addCleanup(p.unpatch)
254-
255- def test_new_account_when_form_validation_fails(self):
256- r = self.post_new_account()
257- self.assertTemplateUsed(r, 'registration/new_account.html')
258- msg = 'It appears that our captcha service was unable to load'
259- self.assertContains(r, msg)
260-
261- def test_new_account_captcha_whitelist(self):
262- email = 'canonicaltest@gmail.com'
263- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
264- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
265- response = self.post_new_account(email=email)
266- self.assertEqual(response.status_code, self.success_status)
267-
268- def test_new_account_captcha_whitelist_with_uuid(self):
269- email = 'canonicaltest+something@gmail.com'
270- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
271- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
272- response = self.post_new_account(email=email)
273- self.assertEqual(response.status_code, self.success_status)
274-
275- def test_new_account_captcha_whitelist_fail(self):
276- email = 'notcanonicaltest@gmail.com'
277- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
278- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
279- response = self.post_new_account(email=email)
280- msg = 'It appears that our captcha service was unable to load'
281- self.assertContains(response, msg)
282-
283- @patch.object(Captcha, '_open')
284- def test_uses_timeline_from_request(self, mock_open):
285- mock_open.return_value = CaptchaResponse(200, StringIO('true\na'))
286- request = Mock()
287- timeline = Timeline()
288- request.META = {'timeline.timeline': timeline}
289- request.POST = {'recaptcha_challenge_field': 'captcha-id'}
290- request.environ = {'REMOTE_ADDR': '127.0.0.1'}
291- ui._verify_captcha_response(None, request, None)
292- self.assertEqual(1, len(timeline.actions))
293- self.assertEqual('captcha-verify', timeline.actions[0].category)
294-
295-
296 class CookiesTestCase(SSOBaseTestCase):
297
298 def setUp(self):
299
300=== modified file 'src/webui/views/registration.py'
301--- src/webui/views/registration.py 2018-05-28 20:15:33 +0000
302+++ src/webui/views/registration.py 2018-10-22 16:30:41 +0000
303@@ -53,7 +53,6 @@
304 requires_cookies,
305 )
306 from webui.views.utils import (
307- add_captcha_settings,
308 display_email_sent,
309 set_session_email,
310 )
311@@ -87,10 +86,6 @@
312 @requires_cookies
313 @require_http_methods(['GET', 'POST'])
314 def new_account(request, token=None):
315- captcha_required = (gargoyle.is_active('CAPTCHA', request) and
316- gargoyle.is_active('CAPTCHA_NEW_ACCOUNT', request))
317- captcha_error = ''
318- captcha_error_message = None
319 rpconfig = get_rpconfig_from_request(request, token)
320
321 def collect_stats(key):
322@@ -108,14 +103,7 @@
323 data = dict((k, v) for k, v in form.cleaned_data.items()
324 if k in ('email', 'password', 'displayname',
325 'username'))
326- data['captcha_id'] = request.POST.get(
327- 'recaptcha_challenge_field'
328- )
329- data['captcha_solution'] = request.POST.get(
330- 'recaptcha_response_field'
331- )
332 # we'll handle our own capture generation
333- data['create_captcha'] = False
334 data['creation_source'] = WEB_CREATION_SOURCE
335 if token:
336 data['oid_token'] = token
337@@ -136,15 +124,6 @@
338 collect_stats('error.email')
339 form._errors['email'] = [VERIFY_EMAIL_MESSAGE]
340
341- except api_errors.CaptchaRequired as e:
342- captcha_required = True
343- collect_stats('captcha_required')
344-
345- except (api_errors.CaptchaFailure, api_errors.CaptchaError) as e:
346- captcha_required = True
347- captcha_error = '&error=' + e.extra.get('captcha_message', '')
348- captcha_error_message = _('Incorrect captcha solution')
349- collect_stats('error.captcha')
350 except Exception as e:
351 return HttpResponseServerError("exception: " + str(e))
352 else:
353@@ -175,12 +154,7 @@
354 'form': form,
355 'rpconfig': rpconfig,
356 'token': token,
357- 'captcha_required': captcha_required,
358- 'captcha_error': captcha_error,
359- 'captcha_error_message': captcha_error_message,
360 }
361- if captcha_required:
362- context = add_captcha_settings(context)
363
364 if form.errors:
365 err = form.errors.get('email', [''])[0]
366
367=== modified file 'src/webui/views/ui.py'
368--- src/webui/views/ui.py 2018-08-24 15:30:53 +0000
369+++ src/webui/views/ui.py 2018-10-22 16:30:41 +0000
370@@ -56,11 +56,7 @@
371
372 )
373 from identityprovider.models import twofactor
374-from identityprovider.models.captcha import (
375- Captcha,
376- CaptchaV2,
377- VerifyCaptchaError
378-)
379+from identityprovider.models.captcha import CaptchaV2
380 from identityprovider.models.const import AccountStatus, AuthTokenType
381 from identityprovider.signals import login_failed, login_succeeded
382 from identityprovider.signed import BadSignedValue
383@@ -94,7 +90,6 @@
384 requires_cookies,
385 )
386 from webui.views import registration
387-from webui.views.utils import add_captcha_settings
388
389
390 ACCOUNT_CREATED = _("Your account was created successfully")
391@@ -150,11 +145,6 @@
392 self, request, token, rpconfig, form, create_account_form=None):
393 context = super(LoginView, self).get_context(
394 request, token=token, rpconfig=rpconfig, form=form)
395- # add captcha and account creation form
396- context['captcha_required'] = (
397- gargoyle.is_active('CAPTCHA', request) and
398- gargoyle.is_active('CAPTCHA_NEW_ACCOUNT', request))
399- context = add_captcha_settings(context)
400 context['create_account_form'] = create_account_form
401 return context
402
403@@ -503,30 +493,6 @@
404 return registration.new_account(request, token)
405
406
407-def _verify_captcha_response(template, request, form):
408- captcha = Captcha(request.POST.get('recaptcha_challenge_field'))
409- captcha_solution = request.POST.get('recaptcha_response_field')
410- email = request.POST.get('email', '')
411- ip_addr = request.environ["REMOTE_ADDR"]
412- try:
413- timer_fn = get_request_timing_function(request)
414- verified = captcha.verify(captcha_solution, ip_addr, email,
415- timer=timer_fn)
416- if verified:
417- return None
418- except VerifyCaptchaError:
419- logger.exception("reCaptcha connection error")
420-
421- # not verified
422- return render(
423- request,
424- template,
425- add_captcha_settings({
426- 'form': form,
427- 'captcha_error': ('&error=%s' % captcha.message),
428- 'captcha_required': True}))
429-
430-
431 @require_twofactor_authenticated(
432 _("Please log in to use this confirmation code"))
433 def confirm_email(request, authtoken, email_address, token=None):
434
435=== modified file 'src/webui/views/utils.py'
436--- src/webui/views/utils.py 2015-05-08 19:25:27 +0000
437+++ src/webui/views/utils.py 2018-10-22 16:30:41 +0000
438@@ -43,10 +43,3 @@
439 def set_session_email(session, email):
440 """Place information about the current token's email in the session"""
441 session['token_email'] = email
442-
443-
444-def add_captcha_settings(context):
445- d = {'CAPTCHA_PUBLIC_KEY': settings.CAPTCHA_PUBLIC_KEY,
446- 'CAPTCHA_API_URL_SECURE': settings.CAPTCHA_API_URL_SECURE}
447- d.update(context)
448- return d