From cdbd76c1cdfd3a228778ace0544726529b5e326b Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 4 Oct 2018 23:33:09 +0200 Subject: [PATCH 3/4] users: add cronjob to delete users (#24430) --- tests/test_users.py | 39 +++++++++++++++++++++++++++++++++++++++ wcs/formdef.py | 8 ++++---- wcs/users.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/tests/test_users.py b/tests/test_users.py index ca039e19..6f6f2a29 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -77,3 +77,42 @@ def test_user_formdef_getattr(): with pytest.raises(AttributeError): user.xxx + + +def test_clean_deleted_users(): + from wcs.formdef import FormDef + from wcs.qommon import storage as st + + User = pub.user_class + + User.wipe() + FormDef.wipe() + + formdef = FormDef() + formdef.name = 'foobar' + formdef.url_name = 'foobar' + formdef.fields = [] + formdef.store() + data_class = formdef.data_class() + + user1 = User() + user1.name = 'Pierre' + user1.deleted = True + user1.store() + + user2 = User() + user2.name = 'Jean' + user2.deleted = True + user2.store() + + formdata1 = data_class() + formdata1.user_id = user1.id + formdata1.store() + + assert User.count() == 2 + + User.clean_deleted_users() + + assert User.count() == 1 + assert len(User.select([st.Equal('name', 'Pierre')])) == 1 + assert len(User.select([st.Equal('name', 'Jean')])) == 0 diff --git a/wcs/formdef.py b/wcs/formdef.py index eee25230..63c79bb9 100644 --- a/wcs/formdef.py +++ b/wcs/formdef.py @@ -1040,7 +1040,7 @@ class FormDef(StorableObject): roles_node = tree.find(node_name) roles = [] setattr(formdef, attr_name, roles) - for child in roles_node.getchildren(): + for child in roles_node: role_id = None value = child.text.encode(charset) if value.startswith('_') or value == 'logged-users': @@ -1061,7 +1061,7 @@ class FormDef(StorableObject): if tree.find('roles') is not None: roles_node = tree.find('roles') formdef.workflow_roles = {} - for child in roles_node.getchildren(): + for child in roles_node: role_key = child.attrib['role_key'] role_id = None value = child.text.encode(charset) @@ -1083,7 +1083,7 @@ class FormDef(StorableObject): if tree.find('geolocations') is not None: geolocations_node = tree.find('geolocations') formdef.geolocations = {} - for child in geolocations_node.getchildren(): + for child in geolocations_node: geoloc_key = child.attrib['key'] geoloc_value = child.text.encode(charset) formdef.geolocations[geoloc_key] = geoloc_value @@ -1091,7 +1091,7 @@ class FormDef(StorableObject): if tree.find('required_authentication_contexts') is not None: node = tree.find('required_authentication_contexts') formdef.required_authentication_contexts = [] - for child in node.getchildren(): + for child in node: formdef.required_authentication_contexts.append(str(child.text)) return formdef diff --git a/wcs/users.py b/wcs/users.py index c769ff60..934e8c59 100644 --- a/wcs/users.py +++ b/wcs/users.py @@ -14,13 +14,18 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . +from quixote import get_publisher + from qommon import _ from qommon.misc import simplify from qommon.storage import StorableObject from qommon import get_cfg +from qommon.cron import CronJob import wcs.qommon.storage as st from qommon.substitution import Substitutions, invalidate_substitution_cache +from qommon.publisher import get_publisher_class + class User(StorableObject): _names = 'users' @@ -221,7 +226,43 @@ class User(StorableObject): def get_full_name(self): return self.display_name + @classmethod + def clean_deleted_users(cls): + # really delete users without any form + if get_publisher().is_using_postgresql(): + cls.clean_deleted_users_sql() + else: + cls.clean_deleted_users_pickle() + + @classmethod + def clean_deleted_users_sql(cls): + from wcs.sql import AnyFormData + deleted = cls.select([st.Equal('deleted', True)]) + deleted_ids = set(u.id for u in deleted) + active_ids = set(f.user_id for f in AnyFormData.select([st.Contains('user_id', deleted_ids)], iterator=True)) + to_delete_ids = deleted_ids - active_ids + for user_id in to_delete_ids: + cls.remove_object(user_id) + + @classmethod + def clean_deleted_users_pickle(cls): + from wcs.formdef import FormDef + + deleted = cls.select([st.Equal('deleted', True)]) + deleted_ids = set(u.id for u in deleted) + active_ids = set() + for formdef in FormDef.select(): + for formdata in formdef.data_class().select([st.Contains('user_id', deleted_ids)], iterator=True): + active_ids.add(formdata.user_id) + to_delete_ids = deleted_ids - active_ids + for user_id in to_delete_ids: + cls.remove_object(user_id) + Substitutions.register('session_user_display_name', category=N_('User'), comment=N_('Session User Display Name')) Substitutions.register('session_user_email', category=N_('User'), comment=N_('Session User Email')) Substitutions.register_dynamic_source(User) + +if get_publisher_class(): + # clean deleted users hourly + get_publisher_class().register_cronjob(CronJob(User.clean_deleted_users, minutes=[20], hours=[6])) -- 2.18.0