0003-views-require-authentication-for-deleting-account-wi.patch
src/authentic2/authenticators.py | ||
---|---|---|
36 | 36 | |
37 | 37 | |
38 | 38 |
class BaseAuthenticator: |
39 |
how = () |
|
40 | ||
39 | 41 |
def __init__(self, show_condition=None, **kwargs): |
40 | 42 |
self.show_condition = show_condition |
41 | 43 | |
... | ... | |
60 | 62 | |
61 | 63 |
class LoginPasswordAuthenticator(BaseAuthenticator): |
62 | 64 |
id = 'password' |
65 |
how = ['password', 'password-on-https'] |
|
63 | 66 |
submit_name = 'login-password-submit' |
64 | 67 |
priority = 0 |
65 | 68 | |
... | ... | |
119 | 122 |
form = authentication_forms.AuthenticationForm( |
120 | 123 |
request=request, data=data, initial=initial, preferred_ous=preferred_ous |
121 | 124 |
) |
125 |
if request.user.is_authenticated and request.login_token.get('action'): |
|
126 |
form.initial['username'] = request.user.username or request.user.email |
|
127 |
form.fields['username'].widget.attrs.pop('autofocus', None) |
|
128 |
form.fields['username'].widget.attrs['readonly'] = 'true' |
|
129 |
form.fields['password'].widget.attrs['autofocus'] = 'autofocus' |
|
122 | 130 |
if app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION: |
123 | 131 |
form.fields['username'].label = _('Username or email') |
124 | 132 |
if app_settings.A2_USERNAME_LABEL: |
src/authentic2/forms/profile.py | ||
---|---|---|
17 | 17 | |
18 | 18 |
from django import forms |
19 | 19 |
from django.forms.models import modelform_factory as dj_modelform_factory |
20 |
from django.utils.translation import ugettext |
|
21 | 20 |
from django.utils.translation import ugettext_lazy as _ |
22 | 21 | |
23 | 22 |
from authentic2 import app_settings, models |
... | ... | |
28 | 27 |
from .utils import NextUrlFormMixin |
29 | 28 | |
30 | 29 | |
31 |
class DeleteAccountForm(forms.Form): |
|
32 |
password = forms.CharField(widget=forms.PasswordInput, label=_("Password")) |
|
33 | ||
34 |
def __init__(self, *args, **kwargs): |
|
35 |
self.user = kwargs.pop('user') |
|
36 |
super().__init__(*args, **kwargs) |
|
37 | ||
38 |
def clean_password(self): |
|
39 |
password = self.cleaned_data.get('password') |
|
40 |
if password and not self.user.check_password(password): |
|
41 |
raise forms.ValidationError(ugettext('Password is invalid')) |
|
42 |
return password |
|
43 | ||
44 | ||
45 | 30 |
class EmailChangeFormNoPassword(forms.Form): |
46 | 31 |
email = ValidatedEmailField(label=_('New email')) |
47 | 32 |
src/authentic2/templates/authentic2/accounts_delete_request.html | ||
---|---|---|
20 | 20 |
Do you really want to delete your account? |
21 | 21 |
{% endblocktrans %} |
22 | 22 |
</p> |
23 |
{% if user.email_verified %} |
|
23 | 24 |
<p> |
24 | 25 |
{% blocktrans trimmed %} |
25 | 26 |
A validation message will be sent to {{ email }}. You will have to visit the |
26 | 27 |
link in this email in order to complete the deletion process. |
27 | 28 |
{% endblocktrans %} |
28 | 29 |
</p> |
29 |
<button class="submit-button" name="submit">{% trans "Send message" %}</button> |
|
30 |
{% endif %} |
|
31 |
<button class="submit-button" name="submit">{% if user.email_verified %}{% trans "Send message" %}{% else %}{% trans "Delete account" %}{% endif %}</button> |
|
30 | 32 |
<button class="cancel-button" name="cancel" formnovalidate>{% trans "Cancel" %}</button> |
31 | 33 |
</form> |
32 | 34 |
{% endblock %} |
src/authentic2/views.py | ||
---|---|---|
17 | 17 |
import collections |
18 | 18 |
import logging |
19 | 19 |
import re |
20 |
import time |
|
20 | 21 |
from email.utils import parseaddr |
21 | 22 | |
22 | 23 |
from django import shortcuts |
... | ... | |
288 | 289 |
def login(request, template_name='authentic2/login.html', redirect_field_name=REDIRECT_FIELD_NAME): |
289 | 290 |
"""Displays the login form and handles the login action.""" |
290 | 291 | |
292 |
request.login_token = token = {} |
|
293 |
if 'token' in request.GET: |
|
294 |
try: |
|
295 |
token.update(signing.loads(request.GET['token'])) |
|
296 |
logger.debug('login: got token %s', token) |
|
297 |
except (signing.SignatureExpired, signing.BadSignature, ValueError): |
|
298 |
logger.warning('login: bad token') |
|
299 |
methods = token.get('methods', []) |
|
300 | ||
291 | 301 |
# redirect user to homepage if already connected, if setting |
292 | 302 |
# A2_LOGIN_REDIRECT_AUTHENTICATED_USERS_TO_HOMEPAGE is True |
293 | 303 |
if request.user.is_authenticated and app_settings.A2_LOGIN_REDIRECT_AUTHENTICATED_USERS_TO_HOMEPAGE: |
... | ... | |
326 | 336 | |
327 | 337 |
# Create blocks |
328 | 338 |
for authenticator in authenticators: |
339 |
if methods and not (authenticator.id in methods or set(authenticator.how) & set(methods)): |
|
340 |
continue |
|
329 | 341 |
# Legacy API |
330 | 342 |
if not hasattr(authenticator, 'login'): |
331 | 343 |
fid = authenticator.id |
... | ... | |
383 | 395 |
block = blocks[0] |
384 | 396 |
authenticator = block['authenticator'] |
385 | 397 |
if hasattr(authenticator, 'autorun'): |
398 |
if 'message' in token: |
|
399 |
messages.info(request, token['message']) |
|
386 | 400 |
return authenticator.autorun(request, block['id']) |
387 | 401 | |
388 | 402 |
# Old frontends API |
... | ... | |
409 | 423 |
redirect_field_name: redirect_to, |
410 | 424 |
} |
411 | 425 |
) |
426 |
if 'message' in token: |
|
427 |
messages.info(request, token['message']) |
|
412 | 428 |
return render(request, template_name, context) |
413 | 429 | |
414 | 430 | |
... | ... | |
1322 | 1338 |
class AccountDeleteView(TemplateView): |
1323 | 1339 |
template_name = 'authentic2/accounts_delete_request.html' |
1324 | 1340 |
title = _('Request account deletion') |
1341 |
last_authentication_max_age = 600 # 10 minutes |
|
1325 | 1342 | |
1326 | 1343 |
def dispatch(self, request, *args, **kwargs): |
1327 | 1344 |
if not app_settings.A2_REGISTRATION_CAN_DELETE_ACCOUNT: |
1328 | 1345 |
return utils_misc.redirect(request, '..') |
1346 |
if not self.request.user.email_verified and not self.has_recent_authentication(): |
|
1347 |
methods = [event['how'] for event in utils_misc.get_authentication_events(request)] |
|
1348 |
return utils_misc.login_require( |
|
1349 |
request, |
|
1350 |
token={ |
|
1351 |
'action': 'account-delete', |
|
1352 |
'message': _('You must re-authenticate to delete your account.'), |
|
1353 |
'methods': methods, |
|
1354 |
}, |
|
1355 |
) |
|
1329 | 1356 |
return super().dispatch(request, *args, **kwargs) |
1330 | 1357 | |
1358 |
def has_recent_authentication(self): |
|
1359 |
age = time.time() - utils_misc.last_authentication_event(request=self.request)['when'] |
|
1360 |
return age < self.last_authentication_max_age |
|
1361 | ||
1331 | 1362 |
def post(self, request, *args, **kwargs): |
1332 | 1363 |
if 'cancel' in request.POST: |
1333 | 1364 |
return utils_misc.redirect(request, 'account_management') |
1334 |
utils_misc.send_account_deletion_code(self.request, self.request.user) |
|
1335 |
messages.info(request, _("An account deletion validation email has been sent to your email address.")) |
|
1365 |
if self.request.user.email_verified: |
|
1366 |
utils_misc.send_account_deletion_code(self.request, self.request.user) |
|
1367 |
messages.info( |
|
1368 |
request, _("An account deletion validation email has been sent to your email address.") |
|
1369 |
) |
|
1370 |
else: |
|
1371 |
deletion_url = utils_misc.build_deletion_url(request, prompt=False) |
|
1372 |
return logout( |
|
1373 |
request, |
|
1374 |
next_url=deletion_url, |
|
1375 |
check_referer=False, |
|
1376 |
) |
|
1336 | 1377 |
return utils_misc.redirect(request, 'account_management') |
1337 | 1378 | |
1338 | 1379 |
def get_context_data(self, **kwargs): |
... | ... | |
1345 | 1386 |
template_name = 'authentic2/accounts_delete_validation.html' |
1346 | 1387 |
title = _('Confirm account deletion') |
1347 | 1388 |
user = None |
1389 |
prompt = True |
|
1348 | 1390 | |
1349 | 1391 |
def dispatch(self, request, *args, **kwargs): |
1350 | 1392 |
try: |
1351 | 1393 |
deletion_token = signing.loads( |
1352 | 1394 |
kwargs['deletion_token'], max_age=app_settings.A2_DELETION_REQUEST_LIFETIME |
1353 | 1395 |
) |
1396 |
self.prompt = deletion_token.get('prompt', self.prompt) |
|
1354 | 1397 |
user_pk = deletion_token['user_pk'] |
1355 | 1398 |
self.user = get_user_model().objects.get(pk=user_pk) |
1356 | 1399 |
# A user account wont be deactived twice |
... | ... | |
1372 | 1415 |
messages.error(request, error) |
1373 | 1416 |
return utils_misc.redirect(request, 'auth_homepage') |
1374 | 1417 | |
1418 |
def get(self, request, *args, **kwargs): |
|
1419 |
if not self.prompt: |
|
1420 |
return self.delete_account(request) |
|
1421 |
return super().get(request, *args, **kwargs) |
|
1422 | ||
1375 | 1423 |
def post(self, request, *args, **kwargs): |
1376 | 1424 |
if 'cancel' not in request.POST: |
1377 |
utils_misc.send_account_deletion_mail(self.request, self.user) |
|
1378 |
logger.info('deletion of account %s performed', self.user) |
|
1379 |
hooks.call_hooks('event', name='delete-account', user=self.user) |
|
1380 |
request.journal.record('user.deletion', user=self.user) |
|
1381 |
is_deleted_user_logged = self.user == request.user |
|
1382 |
self.user.delete() |
|
1383 |
messages.info(request, _('Deletion performed.')) |
|
1384 |
# No real use for cancel_url or next_url here, assuming the link |
|
1385 |
# has been received by email. We instead redirect the user to the |
|
1386 |
# homepage. |
|
1387 |
if is_deleted_user_logged: |
|
1388 |
return logout(request, check_referer=False) |
|
1425 |
return self.delete_account(request) |
|
1426 |
return utils_misc.redirect(request, 'auth_homepage') |
|
1427 | ||
1428 |
def delete_account(self, request): |
|
1429 |
utils_misc.send_account_deletion_mail(self.request, self.user) |
|
1430 |
logger.info('deletion of account %s performed', self.user) |
|
1431 |
hooks.call_hooks('event', name='delete-account', user=self.user) |
|
1432 |
request.journal.record('user.deletion', user=self.user) |
|
1433 |
is_deleted_user_logged = self.user == request.user |
|
1434 |
self.user.delete() |
|
1435 |
messages.info(request, _('Deletion performed.')) |
|
1436 |
# No real use for cancel_url or next_url here, assuming the link |
|
1437 |
# has been received by email. We instead redirect the user to the |
|
1438 |
# homepage. |
|
1439 |
if is_deleted_user_logged: |
|
1440 |
return logout(request, check_referer=False) |
|
1389 | 1441 |
return utils_misc.redirect(request, 'auth_homepage') |
1390 | 1442 | |
1391 | 1443 |
def get_context_data(self, **kwargs): |
src/authentic2_auth_fc/authenticators.py | ||
---|---|---|
28 | 28 | |
29 | 29 |
class FcAuthenticator(BaseAuthenticator): |
30 | 30 |
id = 'fc' |
31 |
how = ['france-connect'] |
|
31 | 32 |
priority = -1 |
32 | 33 | |
33 | 34 |
def enabled(self): |
tests/conftest.py | ||
---|---|---|
126 | 126 |
@pytest.fixture |
127 | 127 |
def simple_user(db, ou1): |
128 | 128 |
return create_user( |
129 |
username='user', first_name='Jôhn', last_name='Dôe', email='user@example.net', ou=get_default_ou() |
|
129 |
username='user', |
|
130 |
first_name='Jôhn', |
|
131 |
last_name='Dôe', |
|
132 |
email='user@example.net', |
|
133 |
ou=get_default_ou(), |
|
130 | 134 |
) |
131 | 135 | |
132 | 136 |
tests/test_views.py | ||
---|---|---|
62 | 62 |
assert resp.location == '/accounts/password/change/' |
63 | 63 | |
64 | 64 | |
65 |
def test_account_delete(app, simple_user, mailoutbox): |
|
66 |
assert simple_user.is_active |
|
67 |
assert len(mailoutbox) == 0 |
|
68 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
69 |
assert simple_user.email in page.text |
|
70 |
page.form.submit(name='submit').follow() |
|
71 |
assert len(mailoutbox) == 1 |
|
72 |
link = get_link_from_mail(mailoutbox[0]) |
|
73 |
assert mailoutbox[0].subject == 'Validate account deletion request on testserver' |
|
74 |
assert [simple_user.email] == mailoutbox[0].to |
|
75 |
page = app.get(link) |
|
76 |
# FIXME: webtest does not set the Referer header, so the logout page will always ask for |
|
77 |
# confirmation under tests |
|
78 |
response = page.form.submit(name='delete') |
|
79 |
assert '_auth_user_id' not in app.session |
|
80 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
81 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
82 |
assert len(mailoutbox) == 2 |
|
83 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
84 |
assert mailoutbox[0].to == [simple_user.email] |
|
85 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
86 |
assert urlparse(response.location).path == '/' |
|
87 | ||
88 | ||
89 |
def test_account_delete_when_logged_out(app, simple_user, mailoutbox): |
|
90 |
assert simple_user.is_active |
|
91 |
assert len(mailoutbox) == 0 |
|
92 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
93 |
page.form.submit(name='submit').follow() |
|
94 |
assert len(mailoutbox) == 1 |
|
95 |
link = get_link_from_mail(mailoutbox[0]) |
|
96 |
logout(app) |
|
97 |
page = app.get(link) |
|
98 |
assert ( |
|
99 |
'You are about to delete the account of <strong>%s</strong>.' % escape(simple_user.get_full_name()) |
|
100 |
in page.text |
|
101 |
) |
|
102 |
response = page.form.submit(name='delete') |
|
103 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
104 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
105 |
assert len(mailoutbox) == 2 |
|
106 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
107 |
assert mailoutbox[0].to == [simple_user.email] |
|
108 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
109 |
assert urlparse(response.location).path == '/' |
|
110 | ||
111 | ||
112 |
def test_account_delete_by_other_user(app, simple_user, user_ou1, mailoutbox): |
|
113 |
assert simple_user.is_active |
|
114 |
assert user_ou1.is_active |
|
115 |
assert len(mailoutbox) == 0 |
|
116 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
117 |
page.form.submit(name='submit').follow() |
|
118 |
assert len(mailoutbox) == 1 |
|
119 |
link = get_link_from_mail(mailoutbox[0]) |
|
120 |
logout(app) |
|
121 |
login(app, user_ou1, path=reverse('account_management')) |
|
122 |
page = app.get(link) |
|
123 |
assert ( |
|
124 |
'You are about to delete the account of <strong>%s</strong>.' % escape(simple_user.get_full_name()) |
|
125 |
in page.text |
|
126 |
) |
|
127 |
response = page.form.submit(name='delete') |
|
128 |
assert app.session['_auth_user_id'] == str(user_ou1.id) |
|
129 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
130 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
131 |
assert len(mailoutbox) == 2 |
|
132 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
133 |
assert mailoutbox[0].to == [simple_user.email] |
|
134 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
135 |
assert urlparse(response.location).path == '/' |
|
136 | ||
137 | ||
138 |
def test_account_delete_fake_token(app, simple_user, mailoutbox): |
|
139 |
response = ( |
|
140 |
app.get(reverse('validate_deletion', kwargs={'deletion_token': 'thisismostlikelynotavalidtoken'})) |
|
141 |
.follow() |
|
142 |
.follow() |
|
143 |
) |
|
144 |
assert "The account deletion request is invalid, try again" in response.text |
|
145 | ||
146 | ||
147 |
def test_account_delete_expired_token(app, simple_user, mailoutbox, freezer): |
|
148 |
freezer.move_to('2019-08-01') |
|
149 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
150 |
page.form.submit(name='submit').follow() |
|
151 |
freezer.move_to('2019-08-04') # Too late... |
|
152 |
link = get_link_from_mail(mailoutbox[0]) |
|
153 |
response = app.get(link).follow() |
|
154 |
assert "The account deletion request is too old, try again" in response.text |
|
155 | ||
156 | ||
157 |
def test_account_delete_valid_token_unexistent_user(app, simple_user, mailoutbox): |
|
158 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
159 |
page.form.submit(name='submit').follow() |
|
160 |
link = get_link_from_mail(mailoutbox[0]) |
|
161 |
simple_user.delete() |
|
162 |
response = app.get(link).follow().follow() |
|
163 |
assert 'This account has previously been deleted.' in response.text |
|
164 | ||
165 | ||
166 |
def test_account_delete_valid_token_inactive_user(app, simple_user, mailoutbox): |
|
167 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
168 |
page.form.submit(name='submit').follow() |
|
169 |
link = get_link_from_mail(mailoutbox[0]) |
|
170 |
simple_user.is_active = False |
|
171 |
simple_user.save() |
|
172 |
response = app.get(link).maybe_follow() |
|
173 |
assert 'This account is inactive, it cannot be deleted.' in response.text |
|
65 |
class TestDeleteAccountEmailVerified: |
|
66 |
@pytest.fixture |
|
67 |
def simple_user(self, simple_user): |
|
68 |
simple_user.email_verified = True |
|
69 |
simple_user.save() |
|
70 |
return simple_user |
|
71 | ||
72 |
def test_account_delete(self, app, simple_user, mailoutbox): |
|
73 |
assert simple_user.is_active |
|
74 |
assert len(mailoutbox) == 0 |
|
75 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
76 |
assert simple_user.email in page.text |
|
77 |
page.form.submit(name='submit').follow() |
|
78 |
assert len(mailoutbox) == 1 |
|
79 |
link = get_link_from_mail(mailoutbox[0]) |
|
80 |
assert mailoutbox[0].subject == 'Validate account deletion request on testserver' |
|
81 |
assert [simple_user.email] == mailoutbox[0].to |
|
82 |
page = app.get(link) |
|
83 |
# FIXME: webtest does not set the Referer header, so the logout page will always ask for |
|
84 |
# confirmation under tests |
|
85 |
response = page.form.submit(name='delete') |
|
86 |
assert '_auth_user_id' not in app.session |
|
87 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
88 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
89 |
assert len(mailoutbox) == 2 |
|
90 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
91 |
assert mailoutbox[0].to == [simple_user.email] |
|
92 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
93 |
assert urlparse(response.location).path == '/' |
|
94 | ||
95 |
def test_account_delete_when_logged_out(self, app, simple_user, mailoutbox): |
|
96 |
assert simple_user.is_active |
|
97 |
assert len(mailoutbox) == 0 |
|
98 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
99 |
page.form.submit(name='submit').follow() |
|
100 |
assert len(mailoutbox) == 1 |
|
101 |
link = get_link_from_mail(mailoutbox[0]) |
|
102 |
logout(app) |
|
103 |
page = app.get(link) |
|
104 |
assert ( |
|
105 |
'You are about to delete the account of <strong>%s</strong>.' |
|
106 |
% escape(simple_user.get_full_name()) |
|
107 |
in page.text |
|
108 |
) |
|
109 |
response = page.form.submit(name='delete') |
|
110 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
111 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
112 |
assert len(mailoutbox) == 2 |
|
113 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
114 |
assert mailoutbox[0].to == [simple_user.email] |
|
115 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
116 |
assert urlparse(response.location).path == '/' |
|
117 | ||
118 |
def test_account_delete_by_other_user(self, app, simple_user, user_ou1, mailoutbox): |
|
119 |
assert simple_user.is_active |
|
120 |
assert user_ou1.is_active |
|
121 |
assert len(mailoutbox) == 0 |
|
122 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
123 |
page.form.submit(name='submit').follow() |
|
124 |
assert len(mailoutbox) == 1 |
|
125 |
link = get_link_from_mail(mailoutbox[0]) |
|
126 |
logout(app) |
|
127 |
login(app, user_ou1, path=reverse('account_management')) |
|
128 |
page = app.get(link) |
|
129 |
assert ( |
|
130 |
'You are about to delete the account of <strong>%s</strong>.' |
|
131 |
% escape(simple_user.get_full_name()) |
|
132 |
in page.text |
|
133 |
) |
|
134 |
response = page.form.submit(name='delete') |
|
135 |
assert app.session['_auth_user_id'] == str(user_ou1.id) |
|
136 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
137 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
138 |
assert len(mailoutbox) == 2 |
|
139 |
assert mailoutbox[1].subject == 'Account deletion on testserver' |
|
140 |
assert mailoutbox[0].to == [simple_user.email] |
|
141 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
142 |
assert urlparse(response.location).path == '/' |
|
143 | ||
144 |
def test_account_delete_fake_token(self, app, simple_user, mailoutbox): |
|
145 |
response = ( |
|
146 |
app.get(reverse('validate_deletion', kwargs={'deletion_token': 'thisismostlikelynotavalidtoken'})) |
|
147 |
.follow() |
|
148 |
.follow() |
|
149 |
) |
|
150 |
assert "The account deletion request is invalid, try again" in response.text |
|
151 | ||
152 |
def test_account_delete_expired_token(self, app, simple_user, mailoutbox, freezer): |
|
153 |
freezer.move_to('2019-08-01') |
|
154 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
155 |
page.form.submit(name='submit').follow() |
|
156 |
freezer.move_to('2019-08-04') # Too late... |
|
157 |
link = get_link_from_mail(mailoutbox[0]) |
|
158 |
response = app.get(link).follow() |
|
159 |
assert "The account deletion request is too old, try again" in response.text |
|
160 | ||
161 |
def test_account_delete_valid_token_unexistent_user(self, app, simple_user, mailoutbox): |
|
162 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
163 |
page.form.submit(name='submit').follow() |
|
164 |
link = get_link_from_mail(mailoutbox[0]) |
|
165 |
simple_user.delete() |
|
166 |
response = app.get(link).follow().follow() |
|
167 |
assert 'This account has previously been deleted.' in response.text |
|
168 | ||
169 |
def test_account_delete_valid_token_inactive_user(self, app, simple_user, mailoutbox): |
|
170 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
171 |
page.form.submit(name='submit').follow() |
|
172 |
link = get_link_from_mail(mailoutbox[0]) |
|
173 |
simple_user.is_active = False |
|
174 |
simple_user.save() |
|
175 |
response = app.get(link).maybe_follow() |
|
176 |
assert 'This account is inactive, it cannot be deleted.' in response.text |
|
177 | ||
178 | ||
179 |
class TestDeleteAccountEmailNotVerified: |
|
180 |
def test_account_delete(self, app, simple_user, mailoutbox): |
|
181 |
assert simple_user.is_active |
|
182 |
assert len(mailoutbox) == 0 |
|
183 |
page = login(app, simple_user, path=reverse('delete_account')) |
|
184 |
response = page.form.submit(name='submit').follow() |
|
185 |
assert '_auth_user_id' not in app.session |
|
186 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
187 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
188 |
assert len(mailoutbox) == 1 |
|
189 |
assert mailoutbox[0].subject == 'Account deletion on testserver' |
|
190 |
assert mailoutbox[0].to == [simple_user.email] |
|
191 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
192 |
assert urlparse(response.location).path == '/' |
|
193 | ||
194 |
def test_account_delete_old_authentication(self, app, simple_user, mailoutbox, freezer): |
|
195 |
assert simple_user.is_active |
|
196 |
assert len(mailoutbox) == 0 |
|
197 |
login(app, simple_user) |
|
198 |
freezer.move_to(datetime.timedelta(hours=1)) |
|
199 |
redirect = app.get('/accounts/delete/') |
|
200 |
login_page = redirect.follow() |
|
201 |
assert 'You must re-authenticate' in login_page |
|
202 |
login_page.form.set('password', simple_user.username) |
|
203 |
page = login_page.form.submit(name='login-password-submit').follow() |
|
204 |
response = page.form.submit(name='submit').follow() |
|
205 |
assert '_auth_user_id' not in app.session |
|
206 |
assert User.objects.filter(id=simple_user.id).count() == 0 |
|
207 |
assert DeletedUser.objects.filter(old_user_id=simple_user.id).count() == 1 |
|
208 |
assert len(mailoutbox) == 1 |
|
209 |
assert mailoutbox[0].subject == 'Account deletion on testserver' |
|
210 |
assert mailoutbox[0].to == [simple_user.email] |
|
211 |
assert "Deletion performed" in str(response) # Set-Cookie: messages.. |
|
212 |
assert urlparse(response.location).path == '/' |
|
174 | 213 | |
175 | 214 | |
176 | 215 |
def test_login_invalid_next(app): |
177 |
- |