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

Proposed by Maximiliano Bertacchini
Status: Rejected
Rejected by: Maximiliano Bertacchini
Proposed branch: lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha
Merge into: lp:canonical-identity-provider/release
Prerequisite: lp:~maxiberta/canonical-identity-provider/disable-user-registration-api
Diff against target: 901 lines (+13/-561)
13 files modified
django_project/settings_devel.py (+0/-1)
src/api/v10/forms.py (+0/-23)
src/api/v10/handlers.py (+10/-20)
src/api/v10/tests/test_forms.py (+0/-24)
src/api/v20/handlers.py (+0/-40)
src/api/v20/tests/test_handlers.py (+1/-195)
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
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+356870@code.launchpad.net

Commit message

Drop captcha from account registration (API & 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
Maximiliano Bertacchini (maxiberta) wrote :

Will split in 3 MPs.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'django_project/settings_devel.py'
2--- django_project/settings_devel.py 2018-02-15 13:17:06 +0000
3+++ django_project/settings_devel.py 2018-10-16 20:30:58 +0000
4@@ -22,7 +22,6 @@
5 'TWOFACTOR': {'is_active': True},
6 'CAN_VIEW_SUPPORT_PHONE': {'is_active': True},
7 'CAPTCHA': {'is_active': False},
8- 'CAPTCHA_NEW_ACCOUNT': {'is_active': False},
9 'PREFLIGHT': {'is_active': True},
10 'LOGIN_BY_TOKEN': {'is_active': True},
11 'SSH_KEY_INTEGRATION': {'is_active': False},
12
13=== modified file 'src/api/v10/forms.py'
14--- src/api/v10/forms.py 2015-05-08 19:25:27 +0000
15+++ src/api/v10/forms.py 2018-10-16 20:30:58 +0000
16@@ -5,17 +5,12 @@
17
18 from __future__ import unicode_literals
19
20-from django import forms
21 from django.forms import fields
22-from django.utils.translation import ugettext_lazy as _
23
24 from identityprovider.forms import NewAccountForm
25-from identityprovider.models.captcha import Captcha
26
27
28 class WebserviceCreateAccountForm(NewAccountForm):
29- captcha_id = fields.CharField(max_length=1024)
30- captcha_solution = fields.CharField(max_length=256)
31 remote_ip = fields.CharField(max_length=256)
32 platform = fields.TypedChoiceField(choices=[
33 ('web', 'Web'), ('desktop', 'Desktop'), ('mobile', 'Mobile')],
34@@ -38,21 +33,3 @@
35 if not validate_redirect_to:
36 validate_redirect_to = None
37 return validate_redirect_to
38-
39- def clean(self):
40- cleaned_data = super(WebserviceCreateAccountForm, self).clean()
41- captcha_id = cleaned_data.get('captcha_id')
42- captcha_solution = cleaned_data.get('captcha_solution')
43-
44- # The remote IP address is absolutely required, and comes from
45- # SSO itself, not from the client. If it's missing, it's a
46- # programming error, and should not be returned to the client
47- # as a validation error. So, we use a normal key lookup here.
48- remote_ip = cleaned_data['remote_ip']
49-
50- captcha = Captcha(captcha_id)
51- email = cleaned_data.get('email', '')
52- if captcha.verify(captcha_solution, remote_ip, email):
53- return cleaned_data
54- # not verified
55- raise forms.ValidationError(_("Wrong captcha solution."))
56
57=== modified file 'src/api/v10/handlers.py'
58--- src/api/v10/handlers.py 2018-10-16 20:30:58 +0000
59+++ src/api/v10/handlers.py 2018-10-16 20:30:58 +0000
60@@ -39,7 +39,6 @@
61 from identityprovider.models.captcha import (
62 Captcha,
63 NewCaptchaError,
64- VerifyCaptchaError,
65 )
66 from identityprovider.models.const import AuthTokenType, EmailStatus
67 from identityprovider.signals import (
68@@ -194,25 +193,16 @@
69 data = request.data
70 data['remote_ip'] = remote_addr
71 form = WebserviceCreateAccountForm(data)
72- try:
73- if not form.is_valid():
74- errors = dict((k, map(unicode, v))
75- for (k, v) in form.errors.items())
76- # XXX: cope with client trimming error messages
77- if unicode(PASSWORD_LEAKED) in errors.get('password', ''):
78- errors['password'] = unicode(_(
79- 'unsafe: leaked by security breach '
80- 'on another website'))
81- result = {'status': 'error', 'errors': errors}
82- return result
83- except VerifyCaptchaError:
84- logger.exception("reCaptcha connection error")
85- msg = unicode(
86- _('Unable to verify captcha. Please try again shortly.'))
87- return {
88- 'status': 'error',
89- 'errors': {'captcha_solution': [msg]}
90- }
91+ if not form.is_valid():
92+ errors = dict((k, map(unicode, v))
93+ for (k, v) in form.errors.items())
94+ # XXX: cope with client trimming error messages
95+ if unicode(PASSWORD_LEAKED) in errors.get('password', ''):
96+ errors['password'] = unicode(_(
97+ 'unsafe: leaked by security breach '
98+ 'on another website'))
99+ result = {'status': 'error', 'errors': errors}
100+ return result
101
102 cleaned_data = form.cleaned_data
103 requested_email = cleaned_data['email']
104
105=== modified file 'src/api/v10/tests/test_forms.py'
106--- src/api/v10/tests/test_forms.py 2015-11-24 14:38:04 +0000
107+++ src/api/v10/tests/test_forms.py 2018-10-16 20:30:58 +0000
108@@ -5,23 +5,12 @@
109
110 from __future__ import unicode_literals
111
112-from django.test.utils import override_settings
113-
114 from api.v10.forms import WebserviceCreateAccountForm
115 from identityprovider.tests.utils import SSOBaseTestCase
116
117
118-@override_settings(CAPTCHA_PUBLIC_KEY='public', CAPTCHA_PRIVATE_KEY='private')
119 class WebServiceCreateAccountFormTestCase(SSOBaseTestCase):
120
121- def setUp(self):
122- super(WebServiceCreateAccountFormTestCase, self).setUp()
123- # patch Captcha so it never hits the real network
124- self.mock_captcha_open = self.patch(
125- 'identityprovider.models.captcha.Captcha._open')
126- self.mock_captcha_open.return_value.is_error = False
127- self.mock_captcha_open.return_value.data.return_value = 'true\nyey'
128-
129 def test_nonascii_password(self):
130 data = {'password': 'Curuzú Cuatiá',
131 'remote_ip': '127.0.0.1'}
132@@ -46,19 +35,6 @@
133 self.assertTrue(form.is_valid())
134 self.assertEqual(form.cleaned_data['platform'], 'desktop')
135
136- def test_captcha_checked_for_whitelist(self):
137- data = {
138- 'email': 'canonicaltest@gmail.com',
139- 'password': 'password1A',
140- 'captcha_id': '1',
141- 'captcha_solution': '2',
142- 'remote_ip': '127.0.0.1',
143- }
144- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
145- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
146- form = WebserviceCreateAccountForm(data)
147- self.assertTrue(form.is_valid())
148-
149 def test_default_cleaned_validate_redirect_to(self):
150 data = {
151 'email': 'some@email.com',
152
153=== modified file 'src/api/v20/handlers.py'
154--- src/api/v20/handlers.py 2018-10-16 20:30:58 +0000
155+++ src/api/v20/handlers.py 2018-10-16 20:30:58 +0000
156@@ -52,7 +52,6 @@
157 Token,
158 twofactor,
159 )
160-from identityprovider.models.captcha import Captcha, VerifyCaptchaError
161 from identityprovider.models.const import (
162 AccountStatus,
163 AuthLogType,
164@@ -61,7 +60,6 @@
165 )
166 from identityprovider.signals import login_failed, login_succeeded
167 from identityprovider.stats import stats
168-from identityprovider.timeline_helpers import get_request_timing_function
169 from identityprovider.utils import redirection_url_for_token
170 from webservices.launchpad import get_lp_ssh_keys
171
172@@ -298,42 +296,6 @@
173 return errors.INVALID_DATA(**missing)
174
175 username = data.get('username')
176- captcha_required = (gargoyle.is_active('CAPTCHA', request) and
177- gargoyle.is_active('CAPTCHA_NEW_ACCOUNT', request))
178-
179- captcha_id = data.get('captcha_id')
180- captcha_solution = data.get('captcha_solution')
181- if not (captcha_solution and captcha_id) and captcha_required:
182- extra = {}
183- if data.get('create_captcha', True):
184- timer = get_request_timing_function(request)
185- try:
186- extra = Captcha.new(timer=timer).serialize()
187- except Exception:
188- logger.exception('failed to create reCaptcha')
189- return errors.CAPTCHA_REQUIRED(**extra)
190-
191- elif captcha_id:
192- captcha = Captcha(captcha_id)
193- verified = False
194- try:
195- timer = get_request_timing_function(request)
196- verified = captcha.verify(
197- captcha_solution, remote_addr, data['email'],
198- timer=timer)
199- except VerifyCaptchaError as e:
200- # only expose HTTP error codes to client
201- code = e.response.code if e.response.code > 200 else None
202- return errors.CAPTCHA_ERROR(
203- recaptcha_reason=e.response.reason,
204- recaptcha_status_code=code,
205- recaptcha_body=e.response.body)
206- except Exception:
207- logger.exception("reCaptcha error")
208-
209- if not verified:
210- message = getattr(captcha, 'message', '')
211- return errors.CAPTCHA_FAILURE(captcha_message=message)
212
213 root_url = request.build_absolute_uri('/')
214 try:
215@@ -368,8 +330,6 @@
216 The newly created account will be verified and passwordless,
217 and the owner will not be able to login until a password reset is done.
218
219- Captcha is completely ignored.
220-
221 """
222 data = request.data
223
224
225=== modified file 'src/api/v20/tests/test_handlers.py'
226--- src/api/v20/tests/test_handlers.py 2018-10-16 20:30:58 +0000
227+++ src/api/v20/tests/test_handlers.py 2018-10-16 20:30:58 +0000
228@@ -7,9 +7,8 @@
229 import time
230 import uuid
231
232-from StringIO import StringIO
233 from urllib import quote, urlencode
234-from urlparse import parse_qsl, urlparse, urlunparse
235+from urlparse import urlparse, urlunparse
236
237 from django.conf import settings
238 from django.contrib.auth.hashers import make_password
239@@ -35,7 +34,6 @@
240 AuthToken,
241 EmailAddress,
242 Token,
243- captcha,
244 )
245 from identityprovider.models.const import (
246 AccountCreationRationale,
247@@ -646,196 +644,6 @@
248 self.assert_correct_account_information(json_body)
249
250
251-@override_settings(CAPTCHA_PUBLIC_KEY='public', CAPTCHA_PRIVATE_KEY='private')
252-class AnonymousAccountRegistrationWithCaptchaHandlerTestCase(BaseTestCase):
253-
254- url = reverse('api-registration')
255-
256- def setUp(self):
257- super(AnonymousAccountRegistrationWithCaptchaHandlerTestCase,
258- self).setUp()
259- p = switches(CAPTCHA=True, CAPTCHA_NEW_ACCOUNT=True)
260- p.patch()
261- self.addCleanup(p.unpatch)
262-
263- self.captcha_response = captcha.CaptchaResponse(
264- code=42, response=StringIO("challenge: '999'"))
265- self.mock_captcha_open = self.patch(
266- 'api.v20.handlers.Captcha._open',
267- return_value=self.captcha_response)
268-
269- self.data = {
270- 'email': self.factory.make_email_address(),
271- 'password': 'asdfASDF1',
272- 'displayname': 'Ricardo the Magnificent',
273- 'captcha_id': '999',
274- 'captcha_solution': 'foo bar',
275- }
276-
277- def test_register_captcha_required(self):
278- captcha_data = {
279- 'captcha_id': '999',
280- 'image_url': settings.CAPTCHA_IMAGE_URL_PATTERN % '999'}
281- del self.data['captcha_id']
282- del self.data['captcha_solution']
283-
284- json_body = self.do_post(self.data, status_code=401)
285-
286- self.assertEqual(json_body['code'], "CAPTCHA_REQUIRED")
287- self.assertIn('A captcha challenge is required', json_body['message'])
288- self.assertIsNone(Account.objects.get_by_email(self.data['email']))
289- self.assertEqual(json_body['extra'], captcha_data)
290-
291- def test_register_captcha_success(self):
292- self.captcha_response.response = StringIO('true\nok')
293- json_body = self.do_post(self.data, status_code=201)
294-
295- url_request = self.mock_captcha_open.call_args[0][0]
296- self.assertEqual(
297- url_request.get_full_url(), settings.CAPTCHA_VERIFY_URL)
298- data = dict(parse_qsl(url_request.data))
299- expected = {
300- 'challenge': '999', 'privatekey': settings.CAPTCHA_PRIVATE_KEY,
301- 'remoteip': '127.0.0.1', 'response': 'foo bar'}
302- self.assertEqual(data, expected)
303-
304- self.assertIn('openid', json_body)
305- self.assertIn('href', json_body)
306- self.assertEqual(json_body['email'], self.data['email'])
307- self.assertEqual(json_body['displayname'], self.data['displayname'])
308- self.assertEqual(json_body['status'], 'Active')
309- self.assertEqual(len(json_body['emails']), 1)
310- self.assertIn(quote(self.data['email'], safe='@'),
311- json_body['emails'][0]['href'])
312-
313- def test_register_captcha_failure(self):
314- self.captcha_response.is_error = False
315-
316- self.data['captcha_id'] = '999'
317- self.data['captcha_solution'] = 'foo bar'
318-
319- json_body = self.do_post(self.data, status_code=403)
320-
321- url_request = self.mock_captcha_open.call_args[0][0]
322- self.assertEqual(
323- url_request.get_full_url(), settings.CAPTCHA_VERIFY_URL)
324- data = dict(parse_qsl(url_request.data))
325- expected = {
326- 'challenge': '999', 'privatekey': settings.CAPTCHA_PRIVATE_KEY,
327- 'remoteip': '127.0.0.1', 'response': 'foo bar'}
328- self.assertEqual(data, expected)
329-
330- self.assertEqual(json_body['code'], "CAPTCHA_FAILURE")
331- self.assertIn(
332- 'Failed response to captcha challenge.', json_body['message'])
333- self.assertIsNone(Account.objects.get_by_email(self.data['email']))
334-
335- def test_register_captcha_whitelist(self):
336- self.data['email'] = 'canonicaltest@gmail.com'
337- self.data['captcha_id'] = '999'
338- self.data['captcha_solution'] = 'foo bar'
339-
340- with self.settings(**OVERRIDES):
341- self.do_post(self.data, status_code=201)
342-
343- self.assertIsNotNone(Account.objects.get_by_email(self.data['email']))
344- self.assertFalse(self.mock_captcha_open.called)
345-
346- def test_register_captcha_whitelist_with_uuid(self):
347- self.data['email'] = 'canonicaltest+something@gmail.com'
348- self.data['captcha_id'] = '999'
349- self.data['captcha_solution'] = 'foo bar'
350-
351- with self.settings(**OVERRIDES):
352- self.do_post(self.data, status_code=201)
353-
354- self.assertIsNotNone(Account.objects.get_by_email(self.data['email']))
355- self.assertFalse(self.mock_captcha_open.called)
356-
357- def test_register_captcha_whitelist_fail(self):
358- self.data['captcha_id'] = '999'
359- self.data['captcha_solution'] = 'foo bar'
360- self.data['email'] = 'notcanonicaltest@gmail.com'
361-
362- self.captcha_response.is_error = False
363- self.captcha_response.response = StringIO('false\nmessage')
364-
365- with self.settings(**OVERRIDES):
366- self.do_post(self.data, status_code=403)
367-
368- self.assertIsNone(Account.objects.get_by_email(self.data['email']))
369- self.assertTrue(self.mock_captcha_open.called)
370-
371- def test_register_captcha_http_error(self):
372- response = captcha.CaptchaResponse(
373- 500, None, "", "Server error", "Unexpected recaptcha error")
374- error = captcha.VerifyCaptchaError(response)
375- self.mock_captcha_open.side_effect = error
376-
377- self.data['captcha_id'] = '999'
378- self.data['captcha_solution'] = 'foo bar'
379-
380- json_body = self.do_post(self.data, status_code=502)
381-
382- self.assertEqual(json_body['code'], "CAPTCHA_ERROR")
383- self.assertIn('Unable to get a valid response from reCaptcha service',
384- json_body['message'])
385- extra = json_body['extra']
386- self.assertEqual(extra['recaptcha_reason'], "Server error")
387- self.assertEqual(extra['recaptcha_status_code'], 500)
388- self.assertEqual(extra['recaptcha_body'], "Unexpected recaptcha error")
389- self.assertIsNone(Account.objects.get_by_email(self.data['email']))
390-
391- def test_register_captcha_network_error(self):
392- response = captcha.CaptchaResponse(111, None, "", "Connection refused")
393- error = captcha.VerifyCaptchaError(response)
394- self.mock_captcha_open.side_effect = error
395-
396- self.data['captcha_id'] = '999'
397- self.data['captcha_solution'] = 'foo bar'
398-
399- json_body = self.do_post(self.data, status_code=502)
400-
401- self.assertEqual(json_body['code'], "CAPTCHA_ERROR")
402- self.assertIn('Unable to get a valid response from reCaptcha service',
403- json_body['message'])
404- extra = json_body['extra']
405- self.assertEqual(extra['recaptcha_reason'], "Connection refused")
406- self.assertEqual(extra['recaptcha_status_code'], None)
407- self.assertEqual(extra['recaptcha_body'], None)
408- self.assertIsNone(Account.objects.get_by_email(self.data['email']))
409-
410- def test_timeline_for_captcha_generation(self):
411- timeline = Timeline()
412- meta = {
413- 'timeline.timeline': timeline,
414- }
415- data = self.data.copy()
416- data.pop('captcha_id')
417- data.pop('captcha_solution')
418- data['create_captcha'] = True
419-
420- self.do_post(data, status_code=401, **meta)
421-
422- self.assertEqual(1, len(timeline.actions))
423- self.assertEqual('captcha-new', timeline.actions[0].category)
424- self.assertEqual(self.mock_captcha_open.call_args[0][0],
425- timeline.actions[0].detail)
426-
427- def test_timeline_for_captcha_verification(self):
428- self.mock_captcha_open.return_value = captcha.CaptchaResponse(
429- 500, StringIO('false\nsomething'))
430- timeline = Timeline()
431- meta = {
432- 'timeline.timeline': timeline,
433- }
434-
435- self.do_post(self.data, status_code=403, **meta)
436-
437- self.assertEqual(1, len(timeline.actions))
438- self.assertEqual('captcha-verify', timeline.actions[0].category)
439-
440-
441 class AccountRegistrationHandlerTestCase(BaseTestCase):
442 url = reverse('api-registration')
443
444@@ -2297,8 +2105,6 @@
445 email = 'foo@foo.com'
446
447 data = {
448- 'captcha_id': 'some-id',
449- 'captcha_solution': 'some solution',
450 'email': 'foo@foo.com',
451 'password': 'some-password-123',
452 'displayname': 'Some User',
453
454=== modified file 'src/webui/templates/registration/_create_account_form.html'
455--- src/webui/templates/registration/_create_account_form.html 2018-07-04 17:51:43 +0000
456+++ src/webui/templates/registration/_create_account_form.html 2018-10-16 20:30:58 +0000
457@@ -50,17 +50,6 @@
458
459 {% include "widgets/passwords.html" with fields=create_form %}
460
461- {% if captcha_required %}
462- <div class="captcha" id="captcha">
463- {% if captcha_error_message %}
464- <span class="error">
465- {{ captcha_error_message }}
466- </span>
467- {% endif %}
468- {% include "widgets/recaptcha.html" %}
469- </div>
470- {% endif %}
471-
472 <div class="input-row{% if create_form.accept_tos.errors %} haserrors{% endif %} accept-tos-input">
473
474 {% if create_form.accept_tos.errors %}
475
476=== removed file 'src/webui/templates/widgets/recaptcha.html'
477--- src/webui/templates/widgets/recaptcha.html 2014-12-11 17:44:29 +0000
478+++ src/webui/templates/widgets/recaptcha.html 1970-01-01 00:00:00 +0000
479@@ -1,43 +0,0 @@
480-{% comment %}
481-Copyright 2010 Canonical Ltd. This software is licensed under the
482-GNU Affero General Public License version 3 (see the file LICENSE).
483-{% endcomment %}
484-
485-{% load i18n %}
486-{% load static_url %}
487-<script type="text/javascript">
488- var RecaptchaOptions = {
489- theme: 'white',
490- custom_translations: {
491- visual_challenge : "{% trans "Get a visual challenge" %}",
492- audio_challenge : "{% trans "Get an audio challenge" %}",
493- refresh_btn : "{% trans "Get a new challenge" %}",
494- instructions_visual : "{% trans "Type the two words:" %}",
495- instructions_audio : "{% trans "Type what you hear:" %}",
496- help_btn : "{% trans "Help" %}",
497- play_again : "{% trans "Play sound again" %}",
498- cant_hear_this : "{% trans "Download sound as MP3" %}",
499- incorrect_try_again : "{% trans "Incorrect. Try again." %}"
500- },
501- };
502-</script>
503-<div {% if captcha_error %}class='captchaError'{% endif %}>
504-{% ifequal captcha_error "&error=no-challenge" %}
505-<p>
506-{% blocktrans with "support_form"|static_url as support_form_url %}
507-It appears that our captcha service was unable to load on this page.
508-This may be caused by a plugin on your browser.
509-Please correct this and try again. If the problem persists, please <a href="{{ support_form_url }}">contact support</a>
510-{% endblocktrans %}
511-</p>
512-{% endifequal %}
513-<script type="text/javascript" src="{{ CAPTCHA_API_URL_SECURE }}/challenge?k={{ CAPTCHA_PUBLIC_KEY }}{{ captcha_error }}">
514-</script>
515-<noscript>
516- <iframe src="{{ CAPTCHA_API_URL_SECURE }}/noscript?k={{ CAPTCHA_PUBLIC_KEY }}" height="300" width="500" frameborder="0" class="recaptcha-noscript">
517- </iframe>
518- <textarea class="recaptcha-challenge-field" name="recaptcha_challenge_field" rows="3" cols="40">
519- </textarea>
520- <input type="hidden" name="recaptcha_response_field" value="manual_challenge">
521-</noscript>
522-</div>
523
524=== modified file 'src/webui/tests/test_views_registration.py'
525--- src/webui/tests/test_views_registration.py 2018-05-28 20:15:33 +0000
526+++ src/webui/tests/test_views_registration.py 2018-10-16 20:30:58 +0000
527@@ -161,16 +161,6 @@
528 ctx = response.context_data
529 self.assertEqual(ctx['form']['email'].value(), 'test@test.com')
530
531- @switches(CAPTCHA=False)
532- def test_get_optional_captcha_switch_off(self):
533- response = self.get()
534- self.assertEqual(response.context_data['captcha_required'], False)
535-
536- @switches(CAPTCHA=True, CAPTCHA_NEW_ACCOUNT=True)
537- def test_get_optional_captcha_switch_on(self):
538- response = self.get()
539- self.assertEqual(response.context_data['captcha_required'], True)
540-
541 def test_post_required_fields(self):
542 response = self.post()
543 self.assert_form_displayed(
544@@ -213,9 +203,6 @@
545 email=self.TESTDATA['email'],
546 password=self.TESTDATA['password'],
547 displayname=self.TESTDATA['displayname'],
548- captcha_id=None,
549- captcha_solution=None,
550- create_captcha=False,
551 creation_source=WEB_CREATION_SOURCE,
552 )
553
554@@ -242,9 +229,6 @@
555 password=self.TESTDATA['password'],
556 displayname=self.TESTDATA['displayname'],
557 username=self.TESTDATA['username'],
558- captcha_id=None,
559- captcha_solution=None,
560- create_captcha=False,
561 creation_source=WEB_CREATION_SOURCE,
562 )
563 self.TESTDATA.pop('username')
564@@ -337,9 +321,6 @@
565 expected_args = dict(email=self.TESTDATA['email'],
566 password=self.TESTDATA['password'],
567 displayname=self.TESTDATA['displayname'],
568- create_captcha=False,
569- captcha_solution=None,
570- captcha_id=None,
571 creation_source='web-flow',
572 oid_token=token)
573 self.mock_api_register.assert_called_once_with(**expected_args)
574@@ -351,38 +332,6 @@
575 self.assert_form_displayed(response, email=VERIFY_EMAIL_MESSAGE)
576 self.assert_stat_calls(['error.email'])
577
578- def test_post_captcha_required(self):
579- exc = api_errors.CaptchaRequired(Mock())
580- self.mock_api_register.side_effect = exc
581- response = self.post(**self.TESTDATA)
582- self.assert_form_displayed(response)
583- self.assertEqual(response.context_data['captcha_required'], True)
584-
585- def test_post_captcha_failure(self):
586- mock_response = Mock()
587- body = {'extra': {'captcha_message': 'XXX'}}
588- exc = api_errors.CaptchaFailure(mock_response, body)
589- self.mock_api_register.side_effect = exc
590-
591- response = self.post(**self.TESTDATA)
592- self.assert_form_displayed(response)
593- self.assertEqual(response.context_data['captcha_required'], True)
594- self.assertEqual(
595- response.context_data['captcha_error'],
596- '&error=XXX')
597- self.assert_stat_calls(['error.captcha'])
598-
599- def test_post_captcha_error(self):
600- mock_response = Mock()
601- body = {}
602- exc = api_errors.CaptchaError(mock_response, body)
603- self.mock_api_register.side_effect = exc
604-
605- response = self.post(**self.TESTDATA)
606- self.assert_form_displayed(response)
607- self.assertEqual(response.context_data['captcha_required'], True)
608- self.assert_stat_calls(['error.captcha'])
609-
610
611 class RegisterTimelineTestCase(
612 SSOBaseTestCase, RegisterTestMixin, TimelineActionMixin):
613
614=== modified file 'src/webui/tests/test_views_ui.py'
615--- src/webui/tests/test_views_ui.py 2018-09-07 21:54:44 +0000
616+++ src/webui/tests/test_views_ui.py 2018-10-16 20:30:58 +0000
617@@ -10,7 +10,6 @@
618 import urllib2
619 from datetime import date
620 from functools import partial
621-from StringIO import StringIO
622 from urlparse import urlsplit
623
624 from django.conf import settings
625@@ -29,7 +28,6 @@
626 from django.test.utils import override_settings
627 from django.urls import reverse
628 from django.utils.html import escape
629-from gargoyle import gargoyle
630 from gargoyle.testutils import switches
631 from mock import Mock, patch
632 from pyquery import PyQuery
633@@ -50,11 +48,7 @@
634 OpenIDRPConfig,
635 twofactor,
636 )
637-from identityprovider.models.captcha import (
638- Captcha,
639- CaptchaResponse,
640- CaptchaV2,
641-)
642+from identityprovider.models.captcha import CaptchaV2
643 from identityprovider.models.const import (
644 AccountStatus,
645 AuthLogType,
646@@ -65,7 +59,6 @@
647 from identityprovider.tests import DEFAULT_USER_PASSWORD
648 from identityprovider.tests.test_auth import AuthLogTestCaseMixin
649 from identityprovider.tests.utils import (
650- MockHandler,
651 SSOBaseTestCase,
652 TimelineActionMixin,
653 )
654@@ -115,9 +108,6 @@
655 'passwordconfirm': 'Testing123',
656 'accept_tos': True
657 }
658- if gargoyle.is_active('CAPTCHA'):
659- data['recaptcha_challenge_field'] = 'ignored'
660- data['recaptcha_response_field'] = 'ignored'
661
662 return self.client.post(url, data, follow=follow)
663
664@@ -132,24 +122,6 @@
665 assert self.client.login(
666 username=self.data['email'], password=self.data['password'])
667
668- def request_when_captcha_fails(self, url, data):
669- class MockCaptcha(object):
670- def __init__(self, *args):
671- pass
672-
673- def verify(self, solution, ip_addr, email):
674- self.message = 'no-challenge'
675- return False
676-
677- @classmethod
678- def new(cls, env):
679- return cls()
680-
681- with patch.object(ui, 'Captcha', MockCaptcha):
682- r = self.client.post(url, data)
683-
684- return r
685-
686
687 @override_settings(LANGUAGE_CODE='es')
688 class SpanishUIViewsTestCase(BaseTestCase):
689@@ -1348,62 +1320,6 @@
690 self.assertFalse(email.is_verified)
691
692
693-@override_settings(CAPTCHA_PRIVATE_KEY='some-private-key')
694-class CaptchaVerificationTestCase(BaseTestCase):
695-
696- success_status = 302
697-
698- def setUp(self):
699- super(CaptchaVerificationTestCase, self).setUp()
700- mock_handler = MockHandler()
701- mock_handler.set_next_response(200, 'false\nno-challenge')
702- self.patch(Captcha, 'opener', new=urllib2.build_opener(mock_handler))
703-
704- p = switches(CAPTCHA=True)
705- p.patch()
706- self.addCleanup(p.unpatch)
707-
708- def test_new_account_when_form_validation_fails(self):
709- r = self.post_new_account()
710- self.assertTemplateUsed(r, 'registration/new_account.html')
711- msg = 'It appears that our captcha service was unable to load'
712- self.assertContains(r, msg)
713-
714- def test_new_account_captcha_whitelist(self):
715- email = 'canonicaltest@gmail.com'
716- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
717- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
718- response = self.post_new_account(email=email)
719- self.assertEqual(response.status_code, self.success_status)
720-
721- def test_new_account_captcha_whitelist_with_uuid(self):
722- email = 'canonicaltest+something@gmail.com'
723- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
724- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
725- response = self.post_new_account(email=email)
726- self.assertEqual(response.status_code, self.success_status)
727-
728- def test_new_account_captcha_whitelist_fail(self):
729- email = 'notcanonicaltest@gmail.com'
730- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
731- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
732- response = self.post_new_account(email=email)
733- msg = 'It appears that our captcha service was unable to load'
734- self.assertContains(response, msg)
735-
736- @patch.object(Captcha, '_open')
737- def test_uses_timeline_from_request(self, mock_open):
738- mock_open.return_value = CaptchaResponse(200, StringIO('true\na'))
739- request = Mock()
740- timeline = Timeline()
741- request.META = {'timeline.timeline': timeline}
742- request.POST = {'recaptcha_challenge_field': 'captcha-id'}
743- request.environ = {'REMOTE_ADDR': '127.0.0.1'}
744- ui._verify_captcha_response(None, request, None)
745- self.assertEqual(1, len(timeline.actions))
746- self.assertEqual('captcha-verify', timeline.actions[0].category)
747-
748-
749 class CookiesTestCase(SSOBaseTestCase):
750
751 def setUp(self):
752
753=== modified file 'src/webui/views/registration.py'
754--- src/webui/views/registration.py 2018-05-28 20:15:33 +0000
755+++ src/webui/views/registration.py 2018-10-16 20:30:58 +0000
756@@ -53,7 +53,6 @@
757 requires_cookies,
758 )
759 from webui.views.utils import (
760- add_captcha_settings,
761 display_email_sent,
762 set_session_email,
763 )
764@@ -87,10 +86,6 @@
765 @requires_cookies
766 @require_http_methods(['GET', 'POST'])
767 def new_account(request, token=None):
768- captcha_required = (gargoyle.is_active('CAPTCHA', request) and
769- gargoyle.is_active('CAPTCHA_NEW_ACCOUNT', request))
770- captcha_error = ''
771- captcha_error_message = None
772 rpconfig = get_rpconfig_from_request(request, token)
773
774 def collect_stats(key):
775@@ -108,14 +103,7 @@
776 data = dict((k, v) for k, v in form.cleaned_data.items()
777 if k in ('email', 'password', 'displayname',
778 'username'))
779- data['captcha_id'] = request.POST.get(
780- 'recaptcha_challenge_field'
781- )
782- data['captcha_solution'] = request.POST.get(
783- 'recaptcha_response_field'
784- )
785 # we'll handle our own capture generation
786- data['create_captcha'] = False
787 data['creation_source'] = WEB_CREATION_SOURCE
788 if token:
789 data['oid_token'] = token
790@@ -136,15 +124,6 @@
791 collect_stats('error.email')
792 form._errors['email'] = [VERIFY_EMAIL_MESSAGE]
793
794- except api_errors.CaptchaRequired as e:
795- captcha_required = True
796- collect_stats('captcha_required')
797-
798- except (api_errors.CaptchaFailure, api_errors.CaptchaError) as e:
799- captcha_required = True
800- captcha_error = '&error=' + e.extra.get('captcha_message', '')
801- captcha_error_message = _('Incorrect captcha solution')
802- collect_stats('error.captcha')
803 except Exception as e:
804 return HttpResponseServerError("exception: " + str(e))
805 else:
806@@ -175,12 +154,7 @@
807 'form': form,
808 'rpconfig': rpconfig,
809 'token': token,
810- 'captcha_required': captcha_required,
811- 'captcha_error': captcha_error,
812- 'captcha_error_message': captcha_error_message,
813 }
814- if captcha_required:
815- context = add_captcha_settings(context)
816
817 if form.errors:
818 err = form.errors.get('email', [''])[0]
819
820=== modified file 'src/webui/views/ui.py'
821--- src/webui/views/ui.py 2018-08-24 15:30:53 +0000
822+++ src/webui/views/ui.py 2018-10-16 20:30:58 +0000
823@@ -56,11 +56,7 @@
824
825 )
826 from identityprovider.models import twofactor
827-from identityprovider.models.captcha import (
828- Captcha,
829- CaptchaV2,
830- VerifyCaptchaError
831-)
832+from identityprovider.models.captcha import CaptchaV2
833 from identityprovider.models.const import AccountStatus, AuthTokenType
834 from identityprovider.signals import login_failed, login_succeeded
835 from identityprovider.signed import BadSignedValue
836@@ -94,7 +90,6 @@
837 requires_cookies,
838 )
839 from webui.views import registration
840-from webui.views.utils import add_captcha_settings
841
842
843 ACCOUNT_CREATED = _("Your account was created successfully")
844@@ -150,11 +145,6 @@
845 self, request, token, rpconfig, form, create_account_form=None):
846 context = super(LoginView, self).get_context(
847 request, token=token, rpconfig=rpconfig, form=form)
848- # add captcha and account creation form
849- context['captcha_required'] = (
850- gargoyle.is_active('CAPTCHA', request) and
851- gargoyle.is_active('CAPTCHA_NEW_ACCOUNT', request))
852- context = add_captcha_settings(context)
853 context['create_account_form'] = create_account_form
854 return context
855
856@@ -503,30 +493,6 @@
857 return registration.new_account(request, token)
858
859
860-def _verify_captcha_response(template, request, form):
861- captcha = Captcha(request.POST.get('recaptcha_challenge_field'))
862- captcha_solution = request.POST.get('recaptcha_response_field')
863- email = request.POST.get('email', '')
864- ip_addr = request.environ["REMOTE_ADDR"]
865- try:
866- timer_fn = get_request_timing_function(request)
867- verified = captcha.verify(captcha_solution, ip_addr, email,
868- timer=timer_fn)
869- if verified:
870- return None
871- except VerifyCaptchaError:
872- logger.exception("reCaptcha connection error")
873-
874- # not verified
875- return render(
876- request,
877- template,
878- add_captcha_settings({
879- 'form': form,
880- 'captcha_error': ('&error=%s' % captcha.message),
881- 'captcha_required': True}))
882-
883-
884 @require_twofactor_authenticated(
885 _("Please log in to use this confirmation code"))
886 def confirm_email(request, authtoken, email_address, token=None):
887
888=== modified file 'src/webui/views/utils.py'
889--- src/webui/views/utils.py 2015-05-08 19:25:27 +0000
890+++ src/webui/views/utils.py 2018-10-16 20:30:58 +0000
891@@ -43,10 +43,3 @@
892 def set_session_email(session, email):
893 """Place information about the current token's email in the session"""
894 session['token_email'] = email
895-
896-
897-def add_captcha_settings(context):
898- d = {'CAPTCHA_PUBLIC_KEY': settings.CAPTCHA_PUBLIC_KEY,
899- 'CAPTCHA_API_URL_SECURE': settings.CAPTCHA_API_URL_SECURE}
900- d.update(context)
901- return d