0001-users-add-cronjob-to-delete-users-24430.patch
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.count() == 4 |
|
167 | ||
168 |
assert {user.name for user in User.select()} == {'Pierre', 'Michel', 'Martin', 'Alain'} |
|
169 | ||
170 |
data_class.wipe() |
|
171 |
card_data_class.wipe() |
|
172 | ||
173 |
get_publisher().clean_deleted_users() |
|
174 | ||
175 |
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 users without any formdata |
|
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 | ||
---|---|---|
3296 | 3296 | |
3297 | 3297 |
return objects |
3298 | 3298 | |
3299 |
@classmethod |
|
3300 |
def get_to_delete_ids(cls): |
|
3301 |
'''Retrieve ids of users which are deleted on the IdP and are no more referenced by any form or card.''' |
|
3302 |
from wcs.carddef import CardDef |
|
3303 |
from wcs.formdef import FormDef |
|
3304 | ||
3305 |
# fetch marked as deleted users |
|
3306 |
conn, cur = get_connection_and_cursor() |
|
3307 |
sql_statement = 'SELECT users.id FROM users WHERE users.deleted_timestamp IS NOT NULL' |
|
3308 |
cur.execute(sql_statement) |
|
3309 |
to_delete_ids = {user_id for user_id, in cur.fetchall()} |
|
3310 |
conn.commit() |
|
3311 | ||
3312 |
# iteratively reduce to_delete_ids by retaining only unreferenced users |
|
3313 |
for carddef in CardDef.select() + FormDef.select(): |
|
3314 |
# keep only user.id not present in form/card_data.user_id |
|
3315 |
data_class = carddef.data_class() |
|
3316 |
sql_statement = '''SELECT users.id |
|
3317 |
FROM users LEFT JOIN %(table)s ON users.id = CAST(%(table)s.user_id AS INTEGER) |
|
3318 |
WHERE users.deleted_timestamp IS NOT NULL |
|
3319 |
AND %(table)s.id IS NULL |
|
3320 |
AND users.id IN %%(to_delete_ids)s |
|
3321 |
''' % { |
|
3322 |
'table': data_class._table_name |
|
3323 |
} |
|
3324 |
cur.execute(sql_statement, {'to_delete_ids': tuple(to_delete_ids)}) |
|
3325 |
to_delete_ids = {user_id for user_id, in cur.fetchall()} |
|
3326 |
# keep only user.id not present in form/card_evolutions.who columns |
|
3327 |
sql_statement = '''SELECT users.id |
|
3328 |
FROM users LEFT JOIN %(table)s ON users.id = CAST(%(table)s.who AS INTEGER) |
|
3329 |
WHERE users.deleted_timestamp IS NOT NULL |
|
3330 |
AND users.id IN %%(to_delete_ids)s |
|
3331 |
AND %(table)s.id IS NULL''' % { |
|
3332 |
'table': '%s_evolutions' % data_class._table_name, |
|
3333 |
} |
|
3334 |
cur.execute(sql_statement, {'to_delete_ids': tuple(to_delete_ids)}) |
|
3335 |
to_delete_ids = {user_id for user_id, in cur.fetchall()} |
|
3336 |
# keep only user.id not present in form/card_data.workflow_roles_array |
|
3337 |
sql_statement = '''SELECT users.id |
|
3338 |
FROM %(table)s AS data |
|
3339 |
JOIN UNNEST(data.workflow_roles_array) AS workflow_role ON 'true' |
|
3340 |
RIGHT JOIN users ON workflow_role.workflow_role = '_user:' || users.id |
|
3341 |
WHERE users.id IN %%(to_delete_ids)s |
|
3342 |
AND users.deleted_timestamp IS NOT NULL AND data.id IS NULL''' % { |
|
3343 |
'table': data_class._table_name, |
|
3344 |
} |
|
3345 |
cur.execute(sql_statement, {'to_delete_ids': tuple(to_delete_ids)}) |
|
3346 |
to_delete_ids = {user_id for user_id, in cur.fetchall()} |
|
3347 |
conn.commit() |
|
3348 |
cur.close() |
|
3349 | ||
3350 |
return to_delete_ids |
|
3351 | ||
3299 | 3352 | |
3300 | 3353 |
class Role(SqlMixin, wcs.roles.Role): |
3301 | 3354 |
_table_name = 'roles' |
3302 |
- |