0001-views-use-one-time-token-for-registration-41792.patch
src/authentic2/urls.py | ||
---|---|---|
30 | 30 |
handler500 = 'authentic2.views.server_error' |
31 | 31 | |
32 | 32 |
accounts_urlpatterns = [ |
33 |
url(r'^activate/(?P<registration_token>[\w: -]+)/$',
|
|
33 |
url(r'^activate/(?P<registration_token>[A-Za-z0-9_ -]+)/$',
|
|
34 | 34 |
views.registration_completion, name='registration_activate'), |
35 | 35 |
url(r'^register/$', |
36 | 36 |
views.RegistrationView.as_view(), |
src/authentic2/utils/__init__.py | ||
---|---|---|
687 | 687 | |
688 | 688 | |
689 | 689 |
def build_activation_url(request, email, next_url=None, ou=None, **kwargs): |
690 |
from authentic2.models import Token |
|
691 | ||
690 | 692 |
data = kwargs.copy() |
691 | 693 |
data['email'] = email |
692 | 694 |
if ou: |
693 | 695 |
data['ou'] = ou.pk |
694 | 696 |
data[REDIRECT_FIELD_NAME] = next_url |
695 |
registration_token = signing.dumps(data) |
|
697 |
lifetime = settings.ACCOUNT_ACTIVATION_DAYS * 3600 * 24 |
|
698 |
# invalidate any token associated with this address |
|
699 |
Token.objects.filter(kind='registration', content__email=email).delete() |
|
700 |
token = Token.create('registration', data, duration=lifetime) |
|
696 | 701 |
activate_url = request.build_absolute_uri( |
697 |
reverse('registration_activate', kwargs={'registration_token': registration_token}))
|
|
702 |
reverse('registration_activate', kwargs={'registration_token': token.uuid_b64url}))
|
|
698 | 703 |
return activate_url |
699 | 704 | |
700 | 705 |
src/authentic2/views.py | ||
---|---|---|
770 | 770 |
password_reset_confirm = PasswordResetConfirmView.as_view() |
771 | 771 | |
772 | 772 | |
773 |
def valid_token(method): |
|
774 |
def f(request, *args, **kwargs): |
|
775 |
try: |
|
776 |
request.token = signing.loads(kwargs['registration_token'].replace(' ', ''), |
|
777 |
max_age=settings.ACCOUNT_ACTIVATION_DAYS * 3600 * 24) |
|
778 |
except signing.SignatureExpired: |
|
779 |
messages.warning(request, _('Your activation key is expired')) |
|
780 |
return utils.redirect(request, 'registration_register') |
|
781 |
except signing.BadSignature: |
|
782 |
messages.warning(request, _('Activation failed')) |
|
783 |
return utils.redirect(request, 'registration_register') |
|
784 |
return method(request, *args, **kwargs) |
|
785 |
return f |
|
786 | ||
787 | ||
788 | 773 |
class BaseRegistrationView(FormView): |
789 | 774 |
form_class = registration_forms.RegistrationForm |
790 | 775 |
template_name = 'registration/registration_form.html' |
... | ... | |
889 | 874 |
return url |
890 | 875 | |
891 | 876 |
def dispatch(self, request, *args, **kwargs): |
892 |
self.token = request.token |
|
877 |
registration_token = kwargs['registration_token'].replace(' ', '') |
|
878 |
try: |
|
879 |
token = models.Token.use('registration', registration_token, delete=False) |
|
880 |
except models.Token.DoesNotExist: |
|
881 |
messages.warning(request, _('Your activation key is unknown or expired')) |
|
882 |
return utils.redirect(request, 'registration_register') |
|
883 |
except (TypeError, ValueError): |
|
884 |
messages.warning(request, _('Activation failed')) |
|
885 |
return utils.redirect(request, 'registration_register') |
|
886 |
self.token_obj = token |
|
887 |
self.token = token.content |
|
888 | ||
893 | 889 |
self.authentication_method = self.token.get('authentication_method', 'email') |
894 |
self.email = request.token['email']
|
|
890 |
self.email = self.token['email']
|
|
895 | 891 |
if 'ou' in self.token: |
896 | 892 |
self.ou = OU.objects.get(pk=self.token['ou']) |
897 | 893 |
else: |
... | ... | |
1087 | 1083 |
ou=self.ou, |
1088 | 1084 |
next_url=self.get_success_url(), |
1089 | 1085 |
**data) |
1086 |
self.token_obj.delete() |
|
1090 | 1087 |
self.request.session['registered_email'] = form.cleaned_data['email'] |
1091 | 1088 |
return utils.redirect(self.request, 'registration_complete') |
1092 | 1089 |
super(RegistrationCompletionView, self).form_valid(form) |
... | ... | |
1095 | 1092 |
def registration_success(self, request, user, form): |
1096 | 1093 |
hooks.call_hooks('event', name='registration', user=user, form=form, view=self, |
1097 | 1094 |
authentication_method=self.authentication_method, |
1098 |
token=request.token, service=self.service) |
|
1095 |
token=self.token, service=self.service) |
|
1096 |
self.token_obj.delete() |
|
1099 | 1097 |
utils.simulate_authentication( |
1100 | 1098 |
request, user, |
1101 | 1099 |
method=self.authentication_method, |
... | ... | |
1123 | 1121 |
request=self.request) |
1124 | 1122 | |
1125 | 1123 | |
1126 |
registration_completion = valid_token(RegistrationCompletionView.as_view())
|
|
1124 |
registration_completion = RegistrationCompletionView.as_view()
|
|
1127 | 1125 | |
1128 | 1126 | |
1129 | 1127 |
class DeleteView(TemplateView): |
tests/test_all.py | ||
---|---|---|
604 | 604 |
self.assertEqual(len(mail.outbox), outbox_level + 1) |
605 | 605 |
outbox_level = len(mail.outbox) |
606 | 606 | |
607 |
# Second registration |
|
608 |
response2 = client.post(reverse('a2-api-register'), |
|
609 |
content_type='application/json', |
|
610 |
data=json.dumps(payload)) |
|
611 |
self.assertEqual(response2.status_code, status.HTTP_202_ACCEPTED) |
|
612 |
self.assertIn('result', response2.data) |
|
613 |
self.assertEqual(response2.data['result'], 1) |
|
614 |
self.assertIn('token', response2.data) |
|
615 |
token2 = response2.data['token'] |
|
616 |
self.assertEqual(len(mail.outbox), outbox_level + 1) |
|
617 | ||
618 |
activation_mail1, activation_mail2 = mail.outbox |
|
619 | ||
620 |
# User side - user click on first email |
|
607 |
# User side - user click on email |
|
621 | 608 |
client = Client() |
622 |
activation_url = get_link_from_mail(activation_mail1)
|
|
609 |
activation_url = get_link_from_mail(mail.outbox[0])
|
|
623 | 610 |
response = client.get(activation_url) |
624 | 611 |
self.assertEqual(response.status_code, status.HTTP_200_OK) |
625 | 612 |
assert utils.make_url(return_url, params={'token': token}) in force_text(response.content) |
... | ... | |
632 | 619 |
self.assertEqual(last_user.ou.slug, self.ou.slug) |
633 | 620 |
self.assertTrue(last_user.check_password(password)) |
634 | 621 | |
635 |
# User click on second email |
|
636 |
client = Client() |
|
637 |
activation_url = get_link_from_mail(activation_mail2) |
|
638 |
response = client.get(activation_url) |
|
639 |
self.assertEqual(response.status_code, status.HTTP_302_FOUND) |
|
640 |
self.assertEqual(response['Location'], |
|
641 |
utils.make_url(return_url, params={'token': token2})) |
|
642 |
self.assertEqual(User.objects.count(), user_count + 1) |
|
643 |
response = client.get(reverse('auth_homepage')) |
|
644 |
self.assertContains(response, username) |
|
645 |
last_user2 = User.objects.order_by('id').last() |
|
646 |
self.assertEqual(User.objects.filter(email=payload['email']).count(), 1) |
|
647 |
self.assertEqual(last_user.id, last_user2.id) |
|
648 |
self.assertEqual(last_user2.username, username) |
|
649 |
self.assertEqual(last_user2.email, email) |
|
650 |
self.assertEqual(last_user2.ou.slug, self.ou.slug) |
|
651 |
self.assertTrue(last_user2.check_password(password)) |
|
652 | ||
653 | 622 |
# Test email is unique with case change |
654 | 623 |
client = test.APIClient() |
655 | 624 |
client.credentials(HTTP_AUTHORIZATION='Basic %s' % cred) |
tests/test_attribute_kinds.py | ||
---|---|---|
79 | 79 | |
80 | 80 | |
81 | 81 |
def test_fr_postcode(db, app, admin, mailoutbox): |
82 | ||
83 |
def register_john(): |
|
84 |
response = app.get('/accounts/register/') |
|
85 |
form = response.form |
|
86 |
form.set('email', 'john.doe@example.com') |
|
87 |
response = form.submit().follow() |
|
88 |
assert 'john.doe@example.com' in response |
|
89 |
return get_link_from_mail(mailoutbox[-1]) |
|
90 | ||
82 | 91 |
Attribute.objects.create(name='postcode', label='postcode', kind='fr_postcode', |
83 | 92 |
asked_on_registration=True) |
84 | 93 |
qs = User.objects.filter(first_name='John') |
85 | 94 | |
86 |
response = app.get('/accounts/register/') |
|
87 |
form = response.form |
|
88 |
form.set('email', 'john.doe@example.com') |
|
89 |
response = form.submit().follow() |
|
90 |
assert 'john.doe@example.com' in response |
|
91 |
url = get_link_from_mail(mailoutbox[0]) |
|
95 |
url = register_john() |
|
92 | 96 |
response = app.get(url) |
93 | 97 | |
94 | 98 |
form = response.form |
... | ... | |
115 | 119 |
assert qs.get().attributes.postcode == '12345' |
116 | 120 |
qs.delete() |
117 | 121 | |
122 |
url = register_john() |
|
118 | 123 |
response = app.get(url) |
119 | 124 |
form = response.form |
120 | 125 |
form.set('first_name', 'John') |
... | ... | |
126 | 131 |
assert qs.get().attributes.postcode == '12345' |
127 | 132 |
qs.delete() |
128 | 133 | |
134 |
url = register_john() |
|
129 | 135 |
response = app.get(url) |
130 | 136 |
form = response.form |
131 | 137 |
form.set('first_name', 'John') |
... | ... | |
182 | 188 | |
183 | 189 | |
184 | 190 |
def test_phone_number(db, app, admin, mailoutbox): |
191 | ||
192 |
def register_john(): |
|
193 |
response = app.get('/accounts/register/') |
|
194 |
form = response.form |
|
195 |
form.set('email', 'john.doe@example.com') |
|
196 |
response = form.submit().follow() |
|
197 |
assert 'john.doe@example.com' in response |
|
198 |
return get_link_from_mail(mailoutbox[-1]) |
|
199 | ||
185 | 200 |
Attribute.objects.create(name='phone_number', label='phone', kind='phone_number', |
186 | 201 |
asked_on_registration=True) |
187 | 202 |
qs = User.objects.filter(first_name='John') |
188 | 203 | |
189 |
response = app.get('/accounts/register/') |
|
190 |
form = response.form |
|
191 |
form.set('email', 'john.doe@example.com') |
|
192 |
response = form.submit().follow() |
|
193 |
assert 'john.doe@example.com' in response |
|
194 |
url = get_link_from_mail(mailoutbox[0]) |
|
195 | ||
204 |
url = register_john() |
|
196 | 205 |
response = app.get(url) |
197 | 206 |
form = response.form |
198 | 207 |
form.set('first_name', 'John') |
... | ... | |
219 | 228 |
assert qs.get().attributes.phone_number == '12345' |
220 | 229 |
qs.delete() |
221 | 230 | |
231 |
url = register_john() |
|
222 | 232 |
response = app.get(url) |
223 | 233 |
form = response.form |
224 | 234 |
form.set('first_name', 'John') |
... | ... | |
230 | 240 |
assert qs.get().attributes.phone_number == '+12345' |
231 | 241 |
qs.delete() |
232 | 242 | |
243 |
url = register_john() |
|
233 | 244 |
response = app.get(url) |
234 | 245 |
form = response.form |
235 | 246 |
form.set('first_name', 'John') |
... | ... | |
241 | 252 |
assert qs.get().attributes.phone_number == '' |
242 | 253 |
qs.delete() |
243 | 254 | |
255 |
url = register_john() |
|
244 | 256 |
response = app.get(url) |
245 | 257 |
form = response.form |
246 | 258 |
form.set('first_name', 'John') |
... | ... | |
306 | 318 | |
307 | 319 | |
308 | 320 |
def test_birthdate(db, app, admin, mailoutbox, freezer): |
321 | ||
322 |
def register_john(): |
|
323 |
response = app.get('/accounts/register/') |
|
324 |
form = response.form |
|
325 |
form.set('email', 'john.doe@example.com') |
|
326 |
response = form.submit().follow() |
|
327 |
assert 'john.doe@example.com' in response |
|
328 |
return get_link_from_mail(mailoutbox[-1]) |
|
329 | ||
309 | 330 |
freezer.move_to('2018-01-01') |
310 | 331 |
Attribute.objects.create(name='birthdate', label='birthdate', kind='birthdate', |
311 | 332 |
asked_on_registration=True) |
312 | 333 |
qs = User.objects.filter(first_name='John') |
313 | 334 | |
314 |
response = app.get('/accounts/register/') |
|
315 |
form = response.form |
|
316 |
form.set('email', 'john.doe@example.com') |
|
317 |
response = form.submit().follow() |
|
318 |
assert 'john.doe@example.com' in response |
|
319 |
url = get_link_from_mail(mailoutbox[0]) |
|
335 |
url = register_john() |
|
320 | 336 |
response = app.get(url) |
321 | 337 | |
322 | 338 |
form = response.form |
... | ... | |
335 | 351 |
assert qs.get().attributes.birthdate == datetime.date(2017, 12, 31) |
336 | 352 |
qs.delete() |
337 | 353 | |
354 |
url = register_john() |
|
338 | 355 |
response = app.get(url) |
339 | 356 |
form = response.form |
340 | 357 |
form.set('first_name', 'John') |
tests/test_registration.py | ||
---|---|---|
261 | 261 |
app.session.flush() |
262 | 262 | |
263 | 263 |
# try again |
264 |
response = app.get(reverse('registration_register')) |
|
265 |
response.form.set('email', 'testbot@entrouvert.com') |
|
266 |
response = response.form.submit() |
|
267 |
link = get_link_from_mail(mailoutbox[2]) |
|
268 | ||
264 | 269 |
response = app.get(link) |
265 | 270 |
response = response.click('create') |
266 | 271 | |
... | ... | |
666 | 671 |
assert hooks.calls['event'][-2]['kwargs']['authentication_method'] == 'another' |
667 | 672 |
assert hooks.calls['event'][-1]['kwargs']['name'] == 'login' |
668 | 673 |
assert hooks.calls['event'][-1]['kwargs']['how'] == 'another' |
674 | ||
675 | ||
676 |
def test_registration_link_unique_use(app, db, mailoutbox): |
|
677 |
models.Attribute.objects.update(disabled=True) |
|
678 | ||
679 |
response = app.get(reverse('registration_register')) |
|
680 |
response.form.set('email', 'testbot@entrouvert.com') |
|
681 |
response = response.form.submit() |
|
682 | ||
683 |
link = get_link_from_mail(mailoutbox[0]) |
|
684 | ||
685 |
response = app.get(link) |
|
686 |
response.form.set('password1', 'T0==toto') |
|
687 | ||
688 |
# accessing multiple times work |
|
689 |
response = app.get(link) |
|
690 |
response.form.set('password1', 'T0==toto') |
|
691 |
response.form.set('password2', 'T0==toto') |
|
692 |
response = response.form.submit().follow() |
|
693 |
assert 'You have just created an account.' in response.text |
|
694 | ||
695 |
response = app.get(link) |
|
696 |
assert urlparse(response['Location']).path == reverse('registration_register') |
|
697 |
response = response.follow() |
|
698 |
assert 'activation key is unknown or expired' in response.text |
|
699 | ||
700 | ||
701 |
def test_double_registration_impossible(app, db, mailoutbox): |
|
702 |
models.Attribute.objects.update(disabled=True) |
|
703 | ||
704 |
for _ in range(2): |
|
705 |
response = app.get(reverse('registration_register')) |
|
706 |
response.form.set('email', 'testbot@entrouvert.com') |
|
707 |
response = response.form.submit() |
|
708 |
assert len(mailoutbox) == 2 |
|
709 | ||
710 |
link1, link2 = get_link_from_mail(mailoutbox[0]), get_link_from_mail(mailoutbox[1]) |
|
711 | ||
712 |
response = app.get(link1) |
|
713 |
assert urlparse(response['Location']).path == reverse('registration_register') |
|
714 |
response = response.follow() |
|
715 |
assert 'activation key is unknown or expired' in response.text |
|
716 | ||
717 |
response = app.get(link2) |
|
718 |
response.form.set('password1', 'T0==toto') |
|
719 |
response.form.set('password2', 'T0==toto') |
|
720 |
response = response.form.submit().follow() |
|
721 |
assert 'You have just created an account.' in response.text |
|
669 |
- |