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

Proposed by Maximiliano Bertacchini
Status: Merged
Approved by: Maximiliano Bertacchini
Approved revision: no longer in the source branch.
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-api-v1
Merge into: lp:canonical-identity-provider/release
Prerequisite: lp:~maxiberta/canonical-identity-provider/disable-user-registration-api
Diff against target: 341 lines (+19/-97)
6 files modified
src/api/templates/api/wadl1.0.xml (+2/-2)
src/api/templates/api/wadl1.1.xml (+2/-2)
src/api/v10/forms.py (+0/-23)
src/api/v10/handlers.py (+10/-22)
src/api/v10/tests/test_forms.py (+0/-24)
src/api/v10/tests/test_handlers.py (+5/-24)
To merge this branch: bzr merge lp:~maxiberta/canonical-identity-provider/drop-account-registration-captcha-api-v1
Reviewer Review Type Date Requested Status
Guillermo Gonzalez Approve
Review via email: mp+356876@code.launchpad.net

Commit message

Drop captcha from account registration API v1.x.

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
Guillermo Gonzalez (verterok) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/api/templates/api/wadl1.0.xml'
2--- src/api/templates/api/wadl1.0.xml 2017-06-12 16:00:39 +0000
3+++ src/api/templates/api/wadl1.0.xml 2018-10-22 16:11:43 +0000
4@@ -562,14 +562,14 @@
5 required="true" fixed="register">
6 <wadl:doc>The name of the operation being invoked.</wadl:doc>
7 </wadl:param>
8- <wadl:param style="query" required="true"
9+ <wadl:param style="query" required="false"
10 name="captcha_solution">
11 <wadl:doc xmlns="http://www.w3.org/1999/xhtml">
12 Solution for the generated captcha.
13 </wadl:doc>
14
15 </wadl:param>
16- <wadl:param style="query" required="true"
17+ <wadl:param style="query" required="false"
18 name="captcha_id">
19 <wadl:doc xmlns="http://www.w3.org/1999/xhtml">
20 ID for the generated captcha
21
22=== modified file 'src/api/templates/api/wadl1.1.xml'
23--- src/api/templates/api/wadl1.1.xml 2017-06-12 16:00:39 +0000
24+++ src/api/templates/api/wadl1.1.xml 2018-10-22 16:11:43 +0000
25@@ -568,14 +568,14 @@
26 required="true" fixed="register">
27 <wadl:doc>The name of the operation being invoked.</wadl:doc>
28 </wadl:param>
29- <wadl:param style="query" required="true"
30+ <wadl:param style="query" required="false"
31 name="captcha_solution">
32 <wadl:doc xmlns="http://www.w3.org/1999/xhtml">
33 Solution for the generated captcha.
34 </wadl:doc>
35
36 </wadl:param>
37- <wadl:param style="query" required="true"
38+ <wadl:param style="query" required="false"
39 name="captcha_id">
40 <wadl:doc xmlns="http://www.w3.org/1999/xhtml">
41 ID for the generated captcha
42
43=== modified file 'src/api/v10/forms.py'
44--- src/api/v10/forms.py 2015-05-08 19:25:27 +0000
45+++ src/api/v10/forms.py 2018-10-22 16:11:43 +0000
46@@ -5,17 +5,12 @@
47
48 from __future__ import unicode_literals
49
50-from django import forms
51 from django.forms import fields
52-from django.utils.translation import ugettext_lazy as _
53
54 from identityprovider.forms import NewAccountForm
55-from identityprovider.models.captcha import Captcha
56
57
58 class WebserviceCreateAccountForm(NewAccountForm):
59- captcha_id = fields.CharField(max_length=1024)
60- captcha_solution = fields.CharField(max_length=256)
61 remote_ip = fields.CharField(max_length=256)
62 platform = fields.TypedChoiceField(choices=[
63 ('web', 'Web'), ('desktop', 'Desktop'), ('mobile', 'Mobile')],
64@@ -38,21 +33,3 @@
65 if not validate_redirect_to:
66 validate_redirect_to = None
67 return validate_redirect_to
68-
69- def clean(self):
70- cleaned_data = super(WebserviceCreateAccountForm, self).clean()
71- captcha_id = cleaned_data.get('captcha_id')
72- captcha_solution = cleaned_data.get('captcha_solution')
73-
74- # The remote IP address is absolutely required, and comes from
75- # SSO itself, not from the client. If it's missing, it's a
76- # programming error, and should not be returned to the client
77- # as a validation error. So, we use a normal key lookup here.
78- remote_ip = cleaned_data['remote_ip']
79-
80- captcha = Captcha(captcha_id)
81- email = cleaned_data.get('email', '')
82- if captcha.verify(captcha_solution, remote_ip, email):
83- return cleaned_data
84- # not verified
85- raise forms.ValidationError(_("Wrong captcha solution."))
86
87=== modified file 'src/api/v10/handlers.py'
88--- src/api/v10/handlers.py 2018-10-22 15:10:24 +0000
89+++ src/api/v10/handlers.py 2018-10-22 16:11:43 +0000
90@@ -36,9 +36,6 @@
91 EmailAddress,
92 get_team_memberships_for_user,
93 )
94-from identityprovider.models.captcha import (
95- VerifyCaptchaError,
96-)
97 from identityprovider.models.const import AuthTokenType, EmailStatus
98 from identityprovider.signals import (
99 account_created,
100@@ -189,25 +186,16 @@
101 data = request.data
102 data['remote_ip'] = request.environ['REMOTE_ADDR']
103 form = WebserviceCreateAccountForm(data)
104- try:
105- if not form.is_valid():
106- errors = dict((k, map(unicode, v))
107- for (k, v) in form.errors.items())
108- # XXX: cope with client trimming error messages
109- if unicode(PASSWORD_LEAKED) in errors.get('password', ''):
110- errors['password'] = unicode(_(
111- 'unsafe: leaked by security breach '
112- 'on another website'))
113- result = {'status': 'error', 'errors': errors}
114- return result
115- except VerifyCaptchaError:
116- logger.exception("reCaptcha connection error")
117- msg = unicode(
118- _('Unable to verify captcha. Please try again shortly.'))
119- return {
120- 'status': 'error',
121- 'errors': {'captcha_solution': [msg]}
122- }
123+ if not form.is_valid():
124+ errors = dict((k, map(unicode, v))
125+ for (k, v) in form.errors.items())
126+ # XXX: cope with client trimming error messages
127+ if unicode(PASSWORD_LEAKED) in errors.get('password', ''):
128+ errors['password'] = unicode(_(
129+ 'unsafe: leaked by security breach '
130+ 'on another website'))
131+ result = {'status': 'error', 'errors': errors}
132+ return result
133
134 cleaned_data = form.cleaned_data
135 requested_email = cleaned_data['email']
136
137=== modified file 'src/api/v10/tests/test_forms.py'
138--- src/api/v10/tests/test_forms.py 2015-11-24 14:38:04 +0000
139+++ src/api/v10/tests/test_forms.py 2018-10-22 16:11:43 +0000
140@@ -5,23 +5,12 @@
141
142 from __future__ import unicode_literals
143
144-from django.test.utils import override_settings
145-
146 from api.v10.forms import WebserviceCreateAccountForm
147 from identityprovider.tests.utils import SSOBaseTestCase
148
149
150-@override_settings(CAPTCHA_PUBLIC_KEY='public', CAPTCHA_PRIVATE_KEY='private')
151 class WebServiceCreateAccountFormTestCase(SSOBaseTestCase):
152
153- def setUp(self):
154- super(WebServiceCreateAccountFormTestCase, self).setUp()
155- # patch Captcha so it never hits the real network
156- self.mock_captcha_open = self.patch(
157- 'identityprovider.models.captcha.Captcha._open')
158- self.mock_captcha_open.return_value.is_error = False
159- self.mock_captcha_open.return_value.data.return_value = 'true\nyey'
160-
161 def test_nonascii_password(self):
162 data = {'password': 'Curuzú Cuatiá',
163 'remote_ip': '127.0.0.1'}
164@@ -46,19 +35,6 @@
165 self.assertTrue(form.is_valid())
166 self.assertEqual(form.cleaned_data['platform'], 'desktop')
167
168- def test_captcha_checked_for_whitelist(self):
169- data = {
170- 'email': 'canonicaltest@gmail.com',
171- 'password': 'password1A',
172- 'captcha_id': '1',
173- 'captcha_solution': '2',
174- 'remote_ip': '127.0.0.1',
175- }
176- pattern = '^canonicaltest(?:\+.+)?@gmail\.com$'
177- with self.settings(EMAIL_WHITELIST_REGEXP_LIST=[pattern]):
178- form = WebserviceCreateAccountForm(data)
179- self.assertTrue(form.is_valid())
180-
181 def test_default_cleaned_validate_redirect_to(self):
182 data = {
183 'email': 'some@email.com',
184
185=== modified file 'src/api/v10/tests/test_handlers.py'
186--- src/api/v10/tests/test_handlers.py 2018-10-22 15:10:24 +0000
187+++ src/api/v10/tests/test_handlers.py 2018-10-22 16:11:43 +0000
188@@ -173,8 +173,7 @@
189 email_validated=False)
190
191 response = self.api.registrations.register(
192- email='register@example.com', password='blogdf3Daa',
193- captcha_solution='solution', captcha_id='id')
194+ email='register@example.com', password='blogdf3Daa')
195
196 self.assertEqual(response['errors'],
197 {'email': ["Email already registered"]})
198@@ -184,8 +183,7 @@
199 email = 'pepe@foo.com'
200
201 response = self.api.registrations.register(
202- email=email, password='MySecretPassword1',
203- captcha_solution='foobar', captcha_id='id')
204+ email=email, password='MySecretPassword1')
205 self.assertEqual(response['status'], 'ok')
206
207 account = Account.objects.get_by_email(email)
208@@ -208,8 +206,7 @@
209 self.factory.make_leaked_credential(email=email, password=password)
210
211 response = self.api.registrations.register(
212- email=email, password=password,
213- captcha_solution='foobar', captcha_id='id')
214+ email=email, password=password)
215 self.assertEqual(response['status'], 'error')
216 self.assertEqual(
217 response['errors'],
218@@ -223,7 +220,6 @@
219
220 response = self.api.registrations.register(
221 email=email, password='MySecretPassword1',
222- captcha_solution='foobar', captcha_id='id',
223 displayname='Test User')
224 self.assertEqual(response['status'], 'ok')
225
226@@ -242,7 +238,6 @@
227
228 response = self.api.registrations.register(
229 email=email, password='MySecretPassword1',
230- captcha_solution='foobar', captcha_id='id',
231 displayname='Test User')
232
233 self.assertEqual(response['status'], 'ok')
234@@ -259,7 +254,6 @@
235 # password: 8 characters long (no uppercase or numbers, only lowercase)
236 response = self.api.registrations.register(
237 email=email, password='abcdefgh',
238- captcha_solution='foobar', captcha_id='id',
239 displayname='Test User')
240 self.assertEqual(response['status'], 'ok')
241
242@@ -271,8 +265,7 @@
243 email = self.factory.make_email_address()
244
245 response = self.api.registrations.register(
246- email=email, password='MySecretPassword1',
247- captcha_solution='foobar', captcha_id='id')
248+ email=email, password='MySecretPassword1')
249 self.assertEqual(response['status'], 'ok')
250 self.assert_new_user_email_sent(email)
251
252@@ -281,7 +274,6 @@
253
254 response = self.api.registrations.register(
255 email=email, password='MySecretPassword1',
256- captcha_solution='foobar', captcha_id='id',
257 platform='mobile')
258 self.assertEqual(response['status'], 'ok')
259 self.assert_new_user_email_sent(email, 'mobile')
260@@ -291,7 +283,6 @@
261
262 response = self.api.registrations.register(
263 email=email, password='MySecretPassword1',
264- captcha_solution='foobar', captcha_id='id',
265 platform='foo')
266
267 self.assertEqual(response['status'], 'error')
268@@ -303,7 +294,6 @@
269
270 response = self.api.registrations.register(
271 email=email_address, password='MySecretPassword1',
272- captcha_solution='foobar', captcha_id='id',
273 platform='desktop')
274
275 self.assertEqual(response['status'], 'ok')
276@@ -314,7 +304,6 @@
277
278 response = self.api.registrations.register(
279 email=email_address, password='MySecretPassword1',
280- captcha_solution='foobar', captcha_id='id',
281 platform='desktop', validate_redirect_to='http://foo')
282
283 self.assertEqual(response['status'], 'ok')
284@@ -327,7 +316,6 @@
285
286 response = self.api.registrations.register(
287 email=email_address, password='MySecretPassword1',
288- captcha_solution='foobar', captcha_id='id',
289 platform='mobile', validate_redirect_to='http://foo')
290
291 self.assertEqual(response['status'], 'ok')
292@@ -341,7 +329,6 @@
293
294 response = self.api.registrations.register(
295 email=email_address, password='MySecretPassword1',
296- captcha_solution='foobar', captcha_id='id',
297 platform='mobile')
298
299 self.assertEqual(response['status'], 'ok')
300@@ -385,9 +372,7 @@
301 self.url,
302 {'ws.op': 'register',
303 'email': email_address,
304- 'password': 'MySecretPassword1',
305- 'captcha_solution': 'foobar',
306- 'captcha_id': 'id'})
307+ 'password': 'MySecretPassword1'})
308
309 @switches(USER_REGISTRATION_API_ENABLED=False)
310 def test_feature_flag_disabled_with_internal_client(self):
311@@ -396,7 +381,6 @@
312 data = {
313 'ws.op': 'register', 'displayname': 'Test User', 'email': email,
314 'password': 'MySecretPassword1',
315- 'captcha_solution': 'foobar', 'captcha_id': 'id',
316 }
317
318 response = self.client.get(self.url, data)
319@@ -413,7 +397,6 @@
320 data = {
321 'ws.op': 'register', 'displayname': 'Test User', 'email': email,
322 'password': 'MySecretPassword1',
323- 'captcha_solution': 'foobar', 'captcha_id': 'id',
324 }
325 headers = {'HTTP_X_FORWARDED_FOR': '127.0.0.1'}
326
327@@ -429,7 +412,6 @@
328 data = {
329 'ws.op': 'register', 'displayname': 'Test User', 'email': email,
330 'password': 'MySecretPassword1',
331- 'captcha_solution': 'foobar', 'captcha_id': 'id',
332 }
333
334 response = self.client.get(self.url, data)
335@@ -446,7 +428,6 @@
336 data = {
337 'ws.op': 'register', 'displayname': 'Test User', 'email': email,
338 'password': 'MySecretPassword1',
339- 'captcha_solution': 'foobar', 'captcha_id': 'id',
340 }
341 headers = {'HTTP_X_FORWARDED_FOR': '127.0.0.1'}
342