Projet

Général

Profil

0001-commands-send-account-deletion-notifications-to-real.patch

Benjamin Dauvergne, 12 juillet 2020 10:37

Télécharger (10,5 ko)

Voir les différences:

Subject: [PATCH] commands: send account deletion notifications to real email
 (#45054)

Add an helper call_command() to test utils to provoke run of on_commit
hooks.
 .../commands/clean-unused-accounts.py         | 14 ++++---
 tests/test_commands.py                        | 41 ++++++++++---------
 tests/utils.py                                | 19 +++++++++
 3 files changed, 49 insertions(+), 25 deletions(-)
src/authentic2/management/commands/clean-unused-accounts.py
21 21
from datetime import timedelta
22 22
from django.contrib.auth import get_user_model
23 23
from django.core.management.base import BaseCommand
24
from django.db.transaction import atomic
24
from django.db import transaction
25 25
from django.db.models import F
26 26
from django.utils import timezone, translation
27 27
from django.utils.six.moves.urllib import parse as urlparse
......
103 103
            'days_to_deletion': days_to_deletion,
104 104
            'login_url': urlparse.urljoin(settings.SITE_BASE_URL, settings.LOGIN_URL),
105 105
        }
106
        with atomic():
106
        with transaction.atomic():
107 107
            if not self.fake:
108 108
                User.objects.filter(pk=user.pk).update(last_account_deletion_alert=self.now)
109 109
            self.send_mail('authentic2/unused_account_alert', user, ctx)
......
114 114
        else:
115 115
            logger.debug('sending mail to %s', user.email)
116 116
            if not self.fake:
117
                send_templated_mail(user.email, prefix, ctx)
117
                email = user.email
118

  
119
                def send_mail():
120
                    send_templated_mail(email, prefix, ctx)
121
                transaction.on_commit(send_mail)
118 122

  
119 123
    def delete_user(self, user):
120 124
        ctx = {'user': user}
121
        with atomic():
125
        with transaction.atomic():
126
            self.send_mail('authentic2/unused_account_delete', user, ctx)
122 127
            if not self.fake:
123 128
                user.mark_as_deleted(timestamp=self.now)
124
            self.send_mail('authentic2/unused_account_delete', user, ctx)
tests/test_commands.py
20 20

  
21 21
import pytest
22 22

  
23
from django.core import management
24 23
from django.utils import six
25 24
from django.utils.timezone import now
26 25
import py
......
28 27
from authentic2_auth_oidc.models import OIDCProvider
29 28
from django_rbac.utils import get_ou_model
30 29

  
31
from .utils import login
30
from .utils import login, call_command
32 31

  
33 32
if six.PY2:
34 33
    FileType = file  # noqa: F821
......
44 43
        return 'pass'
45 44

  
46 45
    monkeypatch.setattr(getpass, 'getpass', _getpass)
47
    management.call_command('changepassword', 'user')
46
    call_command('changepassword', 'user')
48 47
    old_pass = simple_user.password
49 48
    simple_user.refresh_from_db()
50 49
    assert old_pass != simple_user.password
51 50

  
52 51

  
53 52
def test_clean_unused_account(db, simple_user, mailoutbox, freezer):
53
    email = simple_user.email
54 54
    freezer.move_to('2018-01-01')
55 55
    simple_user.ou.clean_unused_accounts_alert = 2
56 56
    simple_user.ou.clean_unused_accounts_deletion = 3
......
59 59
    simple_user.last_login = now() - datetime.timedelta(days=2)
60 60
    simple_user.save()
61 61

  
62
    management.call_command('clean-unused-accounts')
62
    call_command('clean-unused-accounts')
63 63
    simple_user.refresh_from_db()
64 64
    assert not simple_user.deleted
65 65
    assert len(mailoutbox) == 1
66 66

  
67 67
    freezer.move_to('2018-01-01 12:00:00')
68 68
    # no new mail, no deletion
69
    management.call_command('clean-unused-accounts')
69
    call_command('clean-unused-accounts')
70 70
    simple_user.refresh_from_db()
71 71
    assert not simple_user.deleted
72 72
    assert len(mailoutbox) == 1
73 73

  
74 74
    freezer.move_to('2018-01-02')
75
    management.call_command('clean-unused-accounts')
75
    call_command('clean-unused-accounts')
76 76
    simple_user.refresh_from_db()
77 77
    assert simple_user.deleted
78 78
    assert len(mailoutbox) == 2
79
    assert mailoutbox[-1].to == [email]
79 80

  
80 81

  
81 82
def test_clean_unused_account_user_logs_in(app, db, simple_user, mailoutbox, freezer):
......
87 88
    simple_user.last_login = now() - datetime.timedelta(days=2)
88 89
    simple_user.save()
89 90

  
90
    management.call_command('clean-unused-accounts')
91
    call_command('clean-unused-accounts')
91 92
    assert len(mailoutbox) == 1
92 93

  
93 94
    login(app, simple_user)
......
100 101

  
101 102
    # when new alert delay is reached, user gets alerted again
102 103
    freezer.move_to('2018-01-04')
103
    management.call_command('clean-unused-accounts')
104
    call_command('clean-unused-accounts')
104 105
    simple_user.refresh_from_db()
105 106
    assert not simple_user.deleted
106 107
    assert len(mailoutbox) == 2
......
110 111
    simple_user.last_login = now() - datetime.timedelta(days=2)
111 112
    simple_user.save()
112 113

  
113
    management.call_command('clean-unused-accounts')
114
    call_command('clean-unused-accounts')
114 115
    simple_user.refresh_from_db()
115 116
    assert not simple_user.deleted
116 117
    assert len(mailoutbox) == 0
......
125 126
    simple_user.save()
126 127

  
127 128
    # even if account last login in past deletion delay, an alert is always sent first
128
    management.call_command('clean-unused-accounts')
129
    call_command('clean-unused-accounts')
129 130
    simple_user.refresh_from_db()
130 131
    assert not simple_user.deleted
131 132
    assert len(mailoutbox) == 1
132 133

  
133 134
    # and calling again as no effect, since one day must pass before account is deleted
134
    management.call_command('clean-unused-accounts')
135
    call_command('clean-unused-accounts')
135 136
    simple_user.refresh_from_db()
136 137
    assert not simple_user.deleted
137 138
    assert len(mailoutbox) == 1
......
147 148
    simple_user.save()
148 149

  
149 150
    # alert email
150
    management.call_command('clean-unused-accounts')
151
    call_command('clean-unused-accounts')
151 152
    mail = mailoutbox[0]
152 153
    assert formatted in mail.body
153 154
    assert formatted in mail.subject and not '\n' in mail.subject
......
155 156
    # deletion email
156 157
    simple_user.last_account_deletion_alert = now() - datetime.timedelta(days=2)
157 158
    simple_user.save()
158
    management.call_command('clean-unused-accounts')
159
    call_command('clean-unused-accounts')
159 160
    mail = mailoutbox[1]
160 161
    assert formatted in mail.body
161 162

  
......
166 167
    simple_user.ou.save()
167 168
    simple_user.last_login = now() - datetime.timedelta(days=1)
168 169
    simple_user.save()
169
    management.call_command('clean-unused-accounts')
170
    call_command('clean-unused-accounts')
170 171
    mail = mailoutbox[0]
171 172
    assert 'href="http://testserver/login/"' in mail.message().as_string()
172 173

  
173 174

  
174 175
def test_cleanupauthentic(db):
175
    management.call_command('cleanupauthentic')
176
    call_command('cleanupauthentic')
176 177

  
177 178

  
178 179
def test_load_ldif(db, monkeypatch, tmpdir):
......
193 194
    oidc_cmd = importlib.import_module(
194 195
        'authentic2.management.commands.load-ldif')
195 196
    monkeypatch.setattr(oidc_cmd, 'DjangoUserLDIFParser', MockPArser)
196
    management.call_command(
197
    call_command(
197 198
        'load-ldif', ldif.strpath, result='result', extra_attribute={'ldap_attr': 'first_name'})
198 199

  
199 200
    # test ExtraAttributeAction
......
210 211
            pass
211 212

  
212 213
    monkeypatch.setattr(oidc_cmd, 'DjangoUserLDIFParser', MockPArser)
213
    management.call_command(
214
    call_command(
214 215
        'load-ldif', '--extra-attribute', 'ldap_attr', 'first_name',
215 216
        '--result', 'result', ldif.strpath)
216 217

  
......
237 238
    monkeypatch.setattr(oidc_cmd, 'register_issuer', register_issuer)
238 239

  
239 240
    oidc_conf = py.path.local(__file__).dirpath('openid_configuration.json').strpath
240
    management.call_command(
241
    call_command(
241 242
        'oidc-register-issuer', '--openid-configuration', oidc_conf, '--issuer', 'issuer',
242 243
        'somename')
243 244

  
......
246 247

  
247 248

  
248 249
def test_resetpassword(simple_user):
249
    management.call_command('resetpassword', 'user')
250
    call_command('resetpassword', 'user')
250 251
    old_pass = simple_user.password
251 252
    simple_user.refresh_from_db()
252 253
    assert old_pass != simple_user.password
......
254 255

  
255 256
def test_sync_metadata(db):
256 257
    test_file = py.path.local(__file__).dirpath('metadata.xml').strpath
257
    management.call_command('sync-metadata', test_file)
258
    call_command('sync-metadata', test_file)
tests/utils.py
22 22

  
23 23
from lxml import etree
24 24

  
25
from django.core.management import call_command as django_call_command
25 26
from django.test import TestCase
26 27
from django.urls import reverse
27 28
from django.utils.encoding import iri_to_uri, force_text
......
215 216
    select2_field_id = response.pyquery('select')[0].attrib['data-field_id']
216 217
    select2_response = app.get(select2_url, params={'field_id': select2_field_id, 'term': term})
217 218
    return select2_response.json
219

  
220

  
221
@contextmanager
222
def run_on_commit_hooks():
223
    yield
224

  
225
    from django.db import connection
226

  
227
    current_run_on_commit = connection.run_on_commit
228
    connection.run_on_commit = []
229
    while current_run_on_commit:
230
        sids, func = current_run_on_commit.pop(0)
231
        func()
232

  
233

  
234
def call_command(*args, **kwargs):
235
    with run_on_commit_hooks():
236
        return django_call_command(*args, **kwargs)
218
-