Projet

Général

Profil

0001-users-add-cronjob-to-delete-users-24430.patch

Benjamin Dauvergne, 14 octobre 2022 15:59

Télécharger (6,63 ko)

Voir les différences:

Subject: [PATCH] users: add cronjob to delete users (#24430)

 tests/test_users.py | 81 ++++++++++++++++++++++++++++++++++++++++++++-
 wcs/publisher.py    |  6 ++++
 wcs/sql.py          | 61 ++++++++++++++++++++++++++++++++++
 3 files changed, 147 insertions(+), 1 deletion(-)
tests/test_users.py
1
import datetime
1 2
import shutil
3
import time
2 4

  
3 5
import pytest
4
from quixote import cleanup
6
from quixote import cleanup, get_publisher
5 7

  
6 8
from wcs import fields
7 9
from wcs.variables import LazyUser
......
92 94
    with pytest.raises(AttributeError):
93 95
        # noqa pylint: disable=pointless-statement
94 96
        user.xxx
97

  
98

  
99
def test_clean_deleted_users():
100
    from wcs.carddef import CardDef
101
    from wcs.formdef import FormDef
102
    from wcs.workflows import Evolution
103

  
104
    User = pub.user_class
105

  
106
    User.wipe()
107
    FormDef.wipe()
108
    CardDef.wipe()
109

  
110
    formdef = FormDef()
111
    formdef.name = 'foobar'
112
    formdef.url_name = 'foobar'
113
    formdef.fields = []
114
    formdef.store()
115
    data_class = formdef.data_class()
116

  
117
    carddef = CardDef()
118
    carddef.name = 'barfoo'
119
    carddef.url_name = 'barfoo'
120
    carddef.fields = []
121
    carddef.store()
122
    card_data_class = carddef.data_class()
123

  
124
    user1 = User()
125
    user1.name = 'Pierre'
126
    user1.deleted_timestamp = datetime.datetime.now()
127
    user1.store()
128

  
129
    user2 = User()
130
    user2.name = 'Jean'
131
    user2.deleted_timestamp = datetime.datetime.now()
132
    user2.store()
133

  
134
    user3 = User()
135
    user3.name = 'Michel'
136
    user3.deleted_timestamp = datetime.datetime.now()
137
    user3.store()
138

  
139
    user4 = User()
140
    user4.name = 'Martin'
141
    user4.deleted_timestamp = datetime.datetime.now()
142
    user4.store()
143

  
144
    user5 = User()
145
    user5.name = 'Alain'
146
    user5.deleted_timestamp = datetime.datetime.now()
147
    user5.store()
148

  
149
    formdata1 = data_class()
150
    formdata1.user_id = user1.id
151
    evo = Evolution()
152
    evo.time = time.localtime()
153
    evo.who = user4.id
154
    formdata1.evolution = [evo]
155
    formdata1.workflow_roles = {'_received': '_user:%s' % user5.id}
156
    formdata1.store()
157

  
158
    carddata1 = card_data_class()
159
    carddata1.user_id = user3.id
160
    carddata1.store()
161

  
162
    assert User.count() == 5
163

  
164
    get_publisher().clean_deleted_users()
165

  
166
    assert {user.name for user in User.select()} == {'Pierre', 'Michel', 'Martin', 'Alain'}
167

  
168
    data_class.wipe()
169
    card_data_class.wipe()
170

  
171
    get_publisher().clean_deleted_users()
172

  
173
    assert User.count() == 0
wcs/publisher.py
123 123
        cls.register_cronjob(
124 124
            CronJob(cls.update_deprecations_report, name='update_deprecations_report', hours=[2], minutes=[0])
125 125
        )
126
        # once a day delete unreferenced users
127
        cls.register_cronjob(CronJob(cls.clean_deleted_users, name='clean_deleted_users', minutes=[0]))
126 128
        # other jobs
127 129
        data_sources.register_cronjob()
128 130
        formdef.register_cronjobs()
......
511 513
            return value_.get_value()
512 514
        return value_
513 515

  
516
    def clean_deleted_users(self):
517
        for user_id in self.user_class.get_to_delete_ids():
518
            self.user_class.remove_object(user_id)
519

  
514 520

  
515 521
set_publisher_class(WcsPublisher)
516 522
WcsPublisher.register_extra_dir(os.path.join(os.path.dirname(__file__), 'extra'))
wcs/sql.py
3302 3302

  
3303 3303
        return objects
3304 3304

  
3305
    @classmethod
3306
    def get_reference_ids(cls):
3307
        '''Retrieve ids of users reference in some carddata or formdata.'''
3308
        from wcs.carddef import CardDef
3309
        from wcs.formdef import FormDef
3310

  
3311
        referenced_ids = set()
3312

  
3313
        conn, cur = get_connection_and_cursor()
3314

  
3315
        for objectdef in CardDef.select() + FormDef.select():
3316
            data_class = objectdef.data_class()
3317

  
3318
            # referenced in form/card data.user_id
3319
            sql_statement = (
3320
                'SELECT CAST(data.user_id AS INTEGER) FROM %(table)s AS data WHERE data.user_id IS NOT NULL'
3321
                % {
3322
                    'table': data_class._table_name,
3323
                }
3324
            )
3325
            cur.execute(sql_statement)
3326
            referenced_ids.update(user_id for user_id, in cur.fetchall())
3327
            conn.commit()
3328

  
3329
            # referenced in form/card data_evolution.who
3330
            sql_statement = 'SELECT CAST(evolution.who AS INTEGER) FROM %(table)s AS evolution' % {
3331
                'table': '%s_evolutions' % data_class._table_name,
3332
            }
3333
            cur.execute(sql_statement)
3334
            referenced_ids.update(user_id for user_id, in cur.fetchall())
3335
            conn.commit()
3336

  
3337
            # referenced in form/card data.workflow_roles_array
3338
            sql_statement = '''SELECT CAST(SUBSTRING(workflow_role.workflow_role FROM 7) AS INTEGER)
3339
                                 FROM %(table)s AS data, UNNEST(data.workflow_roles_array) AS workflow_role
3340
                                 WHERE SUBSTRING(workflow_role.workflow_role FROM 1 FOR 6) = '_user:' ''' % {
3341
                # users will be referenced as "_user:<user id>" entries in
3342
                # workflow_roles_array, filter on values starting with "_user:"
3343
                # (FROM 1 FOR 6) and extract the id part (FROM 7).
3344
                'table': data_class._table_name,
3345
            }
3346
            cur.execute(sql_statement)
3347
            referenced_ids.update(user_id for user_id, in cur.fetchall())
3348
            conn.commit()
3349
        return referenced_ids
3350

  
3351
    @classmethod
3352
    def get_to_delete_ids(cls):
3353
        '''Retrieve ids of users which are deleted on the IdP and are no more referenced by any form or card.'''
3354

  
3355
        # fetch marked as deleted users
3356
        conn, cur = get_connection_and_cursor()
3357
        sql_statement = 'SELECT users.id FROM users WHERE users.deleted_timestamp IS NOT NULL'
3358
        cur.execute(sql_statement)
3359
        deleted_ids = {user_id for user_id, in cur.fetchall()}
3360
        conn.commit()
3361
        cur.close()
3362

  
3363
        to_delete_ids = deleted_ids.difference(cls.get_reference_ids())
3364
        return to_delete_ids
3365

  
3305 3366

  
3306 3367
class Role(SqlMixin, wcs.roles.Role):
3307 3368
    _table_name = 'roles'
3308
-