From fd529a53c84f8f1ee6f34b2303c0b1a618241441 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 24 Feb 2020 18:18:44 +0100 Subject: [PATCH 01/14] Logging support --- apps/api/__init__.py | 4 ++ apps/api/apps.py | 10 +++++ apps/logs/__init__.py | 4 ++ apps/logs/apps.py | 14 +++++++ apps/logs/models.py | 63 ++++++++++++++++++++++++++++++ apps/logs/signals.py | 68 +++++++++++++++++++++++++++++++++ apps/logs/urls.py | 8 ++++ locale/de/LC_MESSAGES/django.po | 62 ++++++++++++++++++++++++------ locale/fr/LC_MESSAGES/django.po | 62 ++++++++++++++++++++++++++---- note_kfet/settings/base.py | 1 + note_kfet/urls.py | 2 + 11 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 apps/api/__init__.py create mode 100644 apps/api/apps.py create mode 100644 apps/logs/__init__.py create mode 100644 apps/logs/apps.py create mode 100644 apps/logs/models.py create mode 100644 apps/logs/signals.py create mode 100644 apps/logs/urls.py diff --git a/apps/api/__init__.py b/apps/api/__init__.py new file mode 100644 index 00000000..1b17aec6 --- /dev/null +++ b/apps/api/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = 'api.apps.APIConfig' diff --git a/apps/api/apps.py b/apps/api/apps.py new file mode 100644 index 00000000..11d78652 --- /dev/null +++ b/apps/api/apps.py @@ -0,0 +1,10 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class APIConfig(AppConfig): + name = 'api' + verbose_name = _('API') diff --git a/apps/logs/__init__.py b/apps/logs/__init__.py new file mode 100644 index 00000000..58ee5b08 --- /dev/null +++ b/apps/logs/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = 'logs.apps.LogsConfig' diff --git a/apps/logs/apps.py b/apps/logs/apps.py new file mode 100644 index 00000000..f48820c7 --- /dev/null +++ b/apps/logs/apps.py @@ -0,0 +1,14 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class LogsConfig(AppConfig): + name = 'logs' + verbose_name = _('Logs') + + def ready(self): + # noinspection PyUnresolvedReferences + import logs.signals diff --git a/apps/logs/models.py b/apps/logs/models.py new file mode 100644 index 00000000..17e8c710 --- /dev/null +++ b/apps/logs/models.py @@ -0,0 +1,63 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.utils.translation import gettext_lazy as _ +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db import models + + +class Changelog(models.Model): + """ + Store each modification on the database (except sessions and logging), + including creating, editing and deleting models. + """ + + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.PROTECT, + null=True, + verbose_name=_('user'), + ) + + model = models.CharField( + max_length=255, + null=False, + blank=False, + verbose_name=_('model'), + ) + + instance_pk = models.CharField( + max_length=255, + null=False, + blank=False, + verbose_name=_('identifier'), + ) + + previous = models.TextField( + null=True, + verbose_name=_('previous data'), + ) + + data = models.TextField( + null=True, + verbose_name=_('new data'), + ) + + action = models.CharField( # create, edit or delete + max_length=16, + null=False, + blank=False, + verbose_name=_('action'), + ) + + timestamp = models.DateTimeField( + null=False, + blank=False, + auto_now_add=True, + name='timestamp', + verbose_name=_('timestamp'), + ) + + def delete(self, using=None, keep_parents=False): + raise ValidationError(_("Logs cannot be destroyed.")) diff --git a/apps/logs/signals.py b/apps/logs/signals.py new file mode 100644 index 00000000..646739fa --- /dev/null +++ b/apps/logs/signals.py @@ -0,0 +1,68 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +import inspect + +from django.core import serializers +from django.db.models.signals import pre_save, pre_delete +from django.dispatch import receiver +from .models import Changelog + + +def get_user_in_signal(sender, **kwargs): + user = None + for entry in reversed(inspect.stack()): + try: + user = entry[0].f_locals['request'].user + break + except: + pass + + if not user: + print("WARNING: Attempt to save " + str(sender) + " with no user") + + return user + +EXCLUDED = [ + 'Changelog', + 'Migration', + 'Session', + ] + +@receiver(pre_save) +def save_object(sender, instance, **kwargs): + model_name = sender.__name__ + if model_name in EXCLUDED: + return + + previous = sender.objects.filter(pk=instance.pk).all() + + user = get_user_in_signal(sender, **kwargs) + if previous.exists: + previous_json = serializers.serialize('json', previous)[1:-1] + else: + previous_json = None + instance_json = serializers.serialize('json', [instance, ],)[1:-1] + Changelog.objects.create(user=user, + model=model_name, + instance_pk=instance.pk, + previous=previous_json, + data=instance_json, + action=("edit" if previous.exists() else "create") + )#.save() + +@receiver(pre_delete) +def delete_object(sender, instance, **kwargs): + model_name = sender.__name__ + if model_name in EXCLUDED: + return + + user = get_user_in_signal(sender, **kwargs) + instance_json = serializers.serialize('json', [instance, ])[1:-1] + Changelog.objects.create(user=user, + model=model_name, + instance_pk=instance.pk, + previous=instance_json, + data=None, + action="delete" + ).save() diff --git a/apps/logs/urls.py b/apps/logs/urls.py new file mode 100644 index 00000000..6d76674c --- /dev/null +++ b/apps/logs/urls.py @@ -0,0 +1,8 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +app_name = 'logs' + +# TODO User interface +urlpatterns = [ +] diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 3aadf83e..618447da 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-21 13:50+0100\n" +"POT-Creation-Date: 2020-02-24 17:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -82,6 +82,46 @@ msgstr "" msgid "guests" msgstr "" +#: apps/api/apps.py:10 +msgid "API" +msgstr "" + +#: apps/logs/apps.py:10 +msgid "Logs" +msgstr "" + +#: apps/logs/models.py:20 apps/note/models/notes.py:105 +msgid "user" +msgstr "" + +#: apps/logs/models.py:27 +msgid "model" +msgstr "" + +#: apps/logs/models.py:34 +msgid "identifier" +msgstr "" + +#: apps/logs/models.py:39 +msgid "previous data" +msgstr "" + +#: apps/logs/models.py:44 +msgid "new data" +msgstr "" + +#: apps/logs/models.py:51 +msgid "action" +msgstr "" + +#: apps/logs/models.py:59 +msgid "timestamp" +msgstr "" + +#: apps/logs/models.py:63 +msgid "Logs cannot be destroyed." +msgstr "" + #: apps/member/apps.py:10 msgid "member" msgstr "" @@ -244,10 +284,6 @@ msgstr "" msgid "This alias is already taken." msgstr "" -#: apps/note/models/notes.py:105 -msgid "user" -msgstr "" - #: apps/note/models/notes.py:109 msgid "one's note" msgstr "" @@ -358,15 +394,19 @@ msgstr "" msgid "Transfer money from your account to one or others" msgstr "" -#: note_kfet/settings/base.py:148 -msgid "German" -msgstr "" - -#: note_kfet/settings/base.py:149 -msgid "English" +#: apps/note/views.py:143 +msgid "Consommations" msgstr "" #: note_kfet/settings/base.py:150 +msgid "German" +msgstr "" + +#: note_kfet/settings/base.py:151 +msgid "English" +msgstr "" + +#: note_kfet/settings/base.py:152 msgid "French" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index bdf4fc8f..fdb98479 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-21 13:50+0100\n" +"POT-Creation-Date: 2020-02-24 17:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -77,6 +77,50 @@ msgstr "invité" msgid "guests" msgstr "invités" +#: apps/api/apps.py:10 +msgid "API" +msgstr "" + +#: apps/logs/apps.py:10 +msgid "Logs" +msgstr "" + +#: apps/logs/models.py:20 apps/note/models/notes.py:105 +msgid "user" +msgstr "utilisateur" + +#: apps/logs/models.py:27 +msgid "model" +msgstr "Modèle" + +#: apps/logs/models.py:34 +msgid "identifier" +msgstr "Identifiant" + +#: apps/logs/models.py:39 +msgid "previous data" +msgstr "Données précédentes" + +#: apps/logs/models.py:44 +#, fuzzy +#| msgid "end date" +msgid "new data" +msgstr "Nouvelles données" + +#: apps/logs/models.py:51 +#, fuzzy +#| msgid "section" +msgid "action" +msgstr "Action" + +#: apps/logs/models.py:59 +msgid "timestamp" +msgstr "Date" + +#: apps/logs/models.py:63 +msgid "Logs cannot be destroyed." +msgstr "Les logs ne peuvent pas être détruits." + #: apps/member/apps.py:10 msgid "member" msgstr "adhérent" @@ -244,10 +288,6 @@ msgstr "Note" msgid "This alias is already taken." msgstr "Cet alias est déjà pris." -#: apps/note/models/notes.py:105 -msgid "user" -msgstr "utilisateur" - #: apps/note/models/notes.py:109 msgid "one's note" msgstr "note d'un utilisateur" @@ -358,15 +398,21 @@ msgstr "transactions d'adhésion" msgid "Transfer money from your account to one or others" msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres" -#: note_kfet/settings/base.py:148 +#: apps/note/views.py:143 +#, fuzzy +#| msgid "transactions" +msgid "Consommations" +msgstr "transactions" + +#: note_kfet/settings/base.py:150 msgid "German" msgstr "" -#: note_kfet/settings/base.py:149 +#: note_kfet/settings/base.py:151 msgid "English" msgstr "" -#: note_kfet/settings/base.py:150 +#: note_kfet/settings/base.py:152 msgid "French" msgstr "" diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index ab4453e4..eb0787cd 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -61,6 +61,7 @@ INSTALLED_APPS = [ 'member', 'note', 'api', + 'logs', ] LOGIN_REDIRECT_URL = '/note/transfer/' diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 303e229a..fe87cc05 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -21,4 +21,6 @@ urlpatterns = [ # Include Django REST API path('api/', include('api.urls')), + + path('logs/', include('logs.urls')), ] From 1c0bf21cbe07e668ab4f0a7c808ce4592c1b1cca Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 26 Feb 2020 23:34:27 +0100 Subject: [PATCH 02/14] Content type stored in logs instead of model name --- apps/logs/models.py | 6 ++++-- apps/logs/signals.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/logs/models.py b/apps/logs/models.py index 17e8c710..be9a468b 100644 --- a/apps/logs/models.py +++ b/apps/logs/models.py @@ -1,6 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ from django.conf import settings from django.core.exceptions import ValidationError @@ -20,8 +21,9 @@ class Changelog(models.Model): verbose_name=_('user'), ) - model = models.CharField( - max_length=255, + model = models.ForeignKey( + ContentType, + on_delete=models.PROTECT, null=False, blank=False, verbose_name=_('model'), diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 646739fa..2ac25afc 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -3,6 +3,7 @@ import inspect +from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.db.models.signals import pre_save, pre_delete from django.dispatch import receiver @@ -44,7 +45,7 @@ def save_object(sender, instance, **kwargs): previous_json = None instance_json = serializers.serialize('json', [instance, ],)[1:-1] Changelog.objects.create(user=user, - model=model_name, + model=ContentType.objects.get_for_model(instance), instance_pk=instance.pk, previous=previous_json, data=instance_json, From 10854eb3342fe5698a4d440b42eae9742c3c57ab Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 26 Feb 2020 23:55:40 +0100 Subject: [PATCH 03/14] Update some excluded models --- apps/logs/signals.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 2ac25afc..74392d18 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -25,15 +25,17 @@ def get_user_in_signal(sender, **kwargs): return user EXCLUDED = [ - 'Changelog', - 'Migration', - 'Session', + 'changelog', + 'migration', + 'revision', + 'session', + 'version', ] @receiver(pre_save) def save_object(sender, instance, **kwargs): model_name = sender.__name__ - if model_name in EXCLUDED: + if model_name.lower() in EXCLUDED: return previous = sender.objects.filter(pk=instance.pk).all() @@ -55,13 +57,13 @@ def save_object(sender, instance, **kwargs): @receiver(pre_delete) def delete_object(sender, instance, **kwargs): model_name = sender.__name__ - if model_name in EXCLUDED: + if model_name.lower() in EXCLUDED: return user = get_user_in_signal(sender, **kwargs) instance_json = serializers.serialize('json', [instance, ])[1:-1] Changelog.objects.create(user=user, - model=model_name, + model=ContentType.objects.get_for_model(instance), instance_pk=instance.pk, previous=instance_json, data=None, From 1c762e54447bb9fa92b1032289637dd7f39ff03c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 13:34:38 +0100 Subject: [PATCH 04/14] Trying to get IP --- apps/logs/signals.py | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 74392d18..b26da5c1 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -10,19 +10,23 @@ from django.dispatch import receiver from .models import Changelog -def get_user_in_signal(sender, **kwargs): - user = None +def get_request_in_signal(sender, **kwargs): + req = None for entry in reversed(inspect.stack()): try: - user = entry[0].f_locals['request'].user + req = entry[0].f_locals['request'] + # Check if there is a user + # noinspection PyStatementEffect + req.user break except: pass - if not user: + if not req: print("WARNING: Attempt to save " + str(sender) + " with no user") - return user + return req + EXCLUDED = [ 'changelog', @@ -40,12 +44,22 @@ def save_object(sender, instance, **kwargs): previous = sender.objects.filter(pk=instance.pk).all() - user = get_user_in_signal(sender, **kwargs) - if previous.exists: - previous_json = serializers.serialize('json', previous)[1:-1] - else: - previous_json = None - instance_json = serializers.serialize('json', [instance, ],)[1:-1] + req = get_request_in_signal(sender, **kwargs) + try: + user = req.user + if 'X-Real-Ip' in req.headers: + ip = req.headers.get('X-Real-Ip') + else: + ip = req.headers.get('REMOTE_ADDR') + print(ip) + print(req.headers) + except: + user = None + ip = None + + from rest_framework.renderers import JSONRenderer + previous_json = JSONRenderer().render(previous) + instance_json = JSONRenderer().render(instance) Changelog.objects.create(user=user, model=ContentType.objects.get_for_model(instance), instance_pk=instance.pk, @@ -54,13 +68,26 @@ def save_object(sender, instance, **kwargs): action=("edit" if previous.exists() else "create") )#.save() + @receiver(pre_delete) def delete_object(sender, instance, **kwargs): model_name = sender.__name__ if model_name.lower() in EXCLUDED: return - user = get_user_in_signal(sender, **kwargs) + req = get_request_in_signal(sender, **kwargs) + try: + user = req.user + if 'X-Real-Ip' in req.headers: + ip = req.headers.get('X-Real-Ip') + else: + ip = req.headers.get('REMOTE_ADDR') + print(ip) + print(req.headers) + except: + user = None + ip = None + instance_json = serializers.serialize('json', [instance, ])[1:-1] Changelog.objects.create(user=user, model=ContentType.objects.get_for_model(instance), From 5995969a677e6721ab471452b28d71aba3a8ce1e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 14:35:50 +0100 Subject: [PATCH 05/14] Detect migrations --- apps/logs/migrations/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/logs/migrations/__init__.py diff --git a/apps/logs/migrations/__init__.py b/apps/logs/migrations/__init__.py new file mode 100644 index 00000000..e69de29b From 2f51057f4c66fb869bd369b185b745660e2d4b69 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 14:47:34 +0100 Subject: [PATCH 06/14] Add IP address to migrations --- apps/logs/models.py | 6 ++++ apps/logs/signals.py | 68 ++++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/apps/logs/models.py b/apps/logs/models.py index be9a468b..337315bb 100644 --- a/apps/logs/models.py +++ b/apps/logs/models.py @@ -21,6 +21,12 @@ class Changelog(models.Model): verbose_name=_('user'), ) + ip = models.GenericIPAddressField( + null=True, + blank=True, + verbose_name=_("IP Address") + ) + model = models.ForeignKey( ContentType, on_delete=models.PROTECT, diff --git a/apps/logs/signals.py b/apps/logs/signals.py index b26da5c1..bed59eb7 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -10,7 +10,7 @@ from django.dispatch import receiver from .models import Changelog -def get_request_in_signal(sender, **kwargs): +def get_request_in_signal(sender): req = None for entry in reversed(inspect.stack()): try: @@ -28,6 +28,20 @@ def get_request_in_signal(sender, **kwargs): return req +def get_user_and_ip(sender): + req = get_request_in_signal(sender) + try: + user = req.user + if 'HTTP_X_FORWARDED_FOR' in req.META: + ip = req.META.get('HTTP_X_FORWARDED_FOR') + else: + ip = req.META.get('REMOTE_ADDR') + except: + user = None + ip = None + return user, ip + + EXCLUDED = [ 'changelog', 'migration', @@ -44,29 +58,19 @@ def save_object(sender, instance, **kwargs): previous = sender.objects.filter(pk=instance.pk).all() - req = get_request_in_signal(sender, **kwargs) - try: - user = req.user - if 'X-Real-Ip' in req.headers: - ip = req.headers.get('X-Real-Ip') - else: - ip = req.headers.get('REMOTE_ADDR') - print(ip) - print(req.headers) - except: - user = None - ip = None + user, ip = get_user_and_ip(sender) from rest_framework.renderers import JSONRenderer previous_json = JSONRenderer().render(previous) instance_json = JSONRenderer().render(instance) Changelog.objects.create(user=user, - model=ContentType.objects.get_for_model(instance), - instance_pk=instance.pk, - previous=previous_json, - data=instance_json, - action=("edit" if previous.exists() else "create") - )#.save() + ip=ip, + model=ContentType.objects.get_for_model(instance), + instance_pk=instance.pk, + previous=previous_json, + data=instance_json, + action=("edit" if previous.exists() else "create") + ).save() @receiver(pre_delete) @@ -75,24 +79,14 @@ def delete_object(sender, instance, **kwargs): if model_name.lower() in EXCLUDED: return - req = get_request_in_signal(sender, **kwargs) - try: - user = req.user - if 'X-Real-Ip' in req.headers: - ip = req.headers.get('X-Real-Ip') - else: - ip = req.headers.get('REMOTE_ADDR') - print(ip) - print(req.headers) - except: - user = None - ip = None + user, ip = get_user_and_ip(sender) instance_json = serializers.serialize('json', [instance, ])[1:-1] Changelog.objects.create(user=user, - model=ContentType.objects.get_for_model(instance), - instance_pk=instance.pk, - previous=instance_json, - data=None, - action="delete" - ).save() + ip=ip, + model=ContentType.objects.get_for_model(instance), + instance_pk=instance.pk, + previous=instance_json, + data=None, + action="delete" + ).save() From 12ddfad2540d83959f286c9590a883bcec024c1d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 14:59:01 +0100 Subject: [PATCH 07/14] Update JSON --- apps/logs/signals.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index bed59eb7..2d7f2711 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -60,9 +60,8 @@ def save_object(sender, instance, **kwargs): user, ip = get_user_and_ip(sender) - from rest_framework.renderers import JSONRenderer - previous_json = JSONRenderer().render(previous) - instance_json = JSONRenderer().render(instance) + previous_json = serializers.serialize('json', previous)[1:-1] if previous.exists else None + instance_json = serializers.serialize('json', [instance, ])[1:-1] Changelog.objects.create(user=user, ip=ip, model=ContentType.objects.get_for_model(instance), From aecd3fc86f3d0689d99798364f68c05983848663 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 15:30:16 +0100 Subject: [PATCH 08/14] Update excluded models list --- apps/logs/signals.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 2d7f2711..7bef4f60 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -43,17 +43,25 @@ def get_user_and_ip(sender): EXCLUDED = [ - 'changelog', - 'migration', - 'revision', - 'session', - 'version', + 'admin.logentry', + 'authtoken.token', + 'cas_server.user', + 'cas_server.userattributes', + 'contenttypes.contenttype', + 'logs.changelog', + 'migrations.migration', + 'note.noteuser', + 'note.noteclub', + 'note.notespecial', + 'sessions.session', + 'reversion.revision', + 'reversion.version', ] @receiver(pre_save) def save_object(sender, instance, **kwargs): - model_name = sender.__name__ - if model_name.lower() in EXCLUDED: + # noinspection PyProtectedMember + if instance._meta.label_lower in EXCLUDED: return previous = sender.objects.filter(pk=instance.pk).all() @@ -74,8 +82,8 @@ def save_object(sender, instance, **kwargs): @receiver(pre_delete) def delete_object(sender, instance, **kwargs): - model_name = sender.__name__ - if model_name.lower() in EXCLUDED: + # noinspection PyProtectedMember + if instance._meta.label_lower in EXCLUDED: return user, ip = get_user_and_ip(sender) From d316c3130cb603b5812c030dabfba3ce06e0a58c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 15:35:38 +0100 Subject: [PATCH 09/14] Logs are created post save --- apps/logs/signals.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 7bef4f60..ab9b2b29 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -5,7 +5,7 @@ import inspect from django.contrib.contenttypes.models import ContentType from django.core import serializers -from django.db.models.signals import pre_save, pre_delete +from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from .models import Changelog @@ -58,7 +58,7 @@ EXCLUDED = [ 'reversion.version', ] -@receiver(pre_save) +@receiver(post_save) def save_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: @@ -80,7 +80,7 @@ def save_object(sender, instance, **kwargs): ).save() -@receiver(pre_delete) +@receiver(post_delete) def delete_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: From 3c52c1d002d4ce630c0661cd2039d968df8226c3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 15:36:12 +0100 Subject: [PATCH 10/14] Users have now automatically an attached profile to a user (fix an old bug) --- apps/member/apps.py | 13 +++++++++++++ apps/member/signals.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/apps/member/apps.py b/apps/member/apps.py index 2d7f4ab7..20be3e3c 100644 --- a/apps/member/apps.py +++ b/apps/member/apps.py @@ -2,9 +2,22 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig +from django.conf import settings +from django.db.models.signals import pre_save from django.utils.translation import gettext_lazy as _ +from .signals import save_user_note + class MemberConfig(AppConfig): name = 'member' verbose_name = _('member') + + def ready(self): + """ + Define app internal signals to interact with other apps + """ + pre_save.connect( + save_user_note, + sender=settings.AUTH_USER_MODEL, + ) diff --git a/apps/member/signals.py b/apps/member/signals.py index 4e945ad5..5debce4b 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -1,2 +1,15 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + +def save_user_note(instance, created, raw, **_kwargs): + """ + Hook to create and save a profile when an user is updated if it is not registered with the signup form + """ + if raw: + # When provisionning data, do not try to autocreate + return + + if created: + from .models import Profile + Profile.objects.get_or_create(user=instance) + instance.profile.save() From 125f43734546500145fe454d65ac460234e0cc11 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 15:41:15 +0100 Subject: [PATCH 11/14] Profiles are saved after the user --- apps/member/apps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/member/apps.py b/apps/member/apps.py index 20be3e3c..8a6ebe1f 100644 --- a/apps/member/apps.py +++ b/apps/member/apps.py @@ -3,7 +3,7 @@ from django.apps import AppConfig from django.conf import settings -from django.db.models.signals import pre_save +from django.db.models.signals import post_save from django.utils.translation import gettext_lazy as _ from .signals import save_user_note @@ -17,7 +17,7 @@ class MemberConfig(AppConfig): """ Define app internal signals to interact with other apps """ - pre_save.connect( + post_save.connect( save_user_note, sender=settings.AUTH_USER_MODEL, ) From bcee5f8f2f31be0aacdd206b009e520a7043a880 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 15:53:06 +0100 Subject: [PATCH 12/14] Don't log when there is no modification, or only when a user is connected --- apps/logs/signals.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index ab9b2b29..45a0714a 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -5,7 +5,7 @@ import inspect from django.contrib.contenttypes.models import ContentType from django.core import serializers -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import pre_save, pre_delete from django.dispatch import receiver from .models import Changelog @@ -58,7 +58,7 @@ EXCLUDED = [ 'reversion.version', ] -@receiver(post_save) +@receiver(pre_save) def save_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: @@ -68,8 +68,18 @@ def save_object(sender, instance, **kwargs): user, ip = get_user_and_ip(sender) - previous_json = serializers.serialize('json', previous)[1:-1] if previous.exists else None + if user is not None and instance._meta.label_lower == "auth.user" and previous.exists(): + # Don't save last login modifications + if instance.last_login != previous.get().last_login: + return + + previous_json = serializers.serialize('json', previous)[1:-1] if previous.exists() else None instance_json = serializers.serialize('json', [instance, ])[1:-1] + + if previous_json == instance_json: + # No modification + return + Changelog.objects.create(user=user, ip=ip, model=ContentType.objects.get_for_model(instance), @@ -80,7 +90,7 @@ def save_object(sender, instance, **kwargs): ).save() -@receiver(post_delete) +@receiver(pre_delete) def delete_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: From 9cd050e2f02ac721f007fb248634be74a4a7c990 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 16:25:18 +0100 Subject: [PATCH 13/14] Logging is finally processed at post saved, but old instance is querried --- apps/logs/signals.py | 24 +++++++++++++++++------- apps/member/apps.py | 4 ++-- apps/member/signals.py | 4 ++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 45a0714a..55e0f041 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -5,7 +5,7 @@ import inspect from django.contrib.contenttypes.models import ContentType from django.core import serializers -from django.db.models.signals import pre_save, pre_delete +from django.db.models.signals import pre_save, post_save, post_delete from django.dispatch import receiver from .models import Changelog @@ -58,22 +58,32 @@ EXCLUDED = [ 'reversion.version', ] + @receiver(pre_save) +def pre_save_object(sender, instance, **kwargs): + qs = sender.objects.filter(pk=instance.pk).all() + if qs.exists(): + instance._previous = qs.get() + else: + instance._previous = None + + +@receiver(post_save) def save_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: return - previous = sender.objects.filter(pk=instance.pk).all() + previous = instance._previous user, ip = get_user_and_ip(sender) - if user is not None and instance._meta.label_lower == "auth.user" and previous.exists(): + if user is not None and instance._meta.label_lower == "auth.user" and previous: # Don't save last login modifications - if instance.last_login != previous.get().last_login: + if instance.last_login != previous.last_login: return - previous_json = serializers.serialize('json', previous)[1:-1] if previous.exists() else None + previous_json = serializers.serialize('json', [previous, ])[1:-1] if previous else None instance_json = serializers.serialize('json', [instance, ])[1:-1] if previous_json == instance_json: @@ -86,11 +96,11 @@ def save_object(sender, instance, **kwargs): instance_pk=instance.pk, previous=previous_json, data=instance_json, - action=("edit" if previous.exists() else "create") + action=("edit" if previous else "create") ).save() -@receiver(pre_delete) +@receiver(post_delete) def delete_object(sender, instance, **kwargs): # noinspection PyProtectedMember if instance._meta.label_lower in EXCLUDED: diff --git a/apps/member/apps.py b/apps/member/apps.py index 8a6ebe1f..83dfbc40 100644 --- a/apps/member/apps.py +++ b/apps/member/apps.py @@ -6,7 +6,7 @@ from django.conf import settings from django.db.models.signals import post_save from django.utils.translation import gettext_lazy as _ -from .signals import save_user_note +from .signals import save_user_profile class MemberConfig(AppConfig): @@ -18,6 +18,6 @@ class MemberConfig(AppConfig): Define app internal signals to interact with other apps """ post_save.connect( - save_user_note, + save_user_profile, sender=settings.AUTH_USER_MODEL, ) diff --git a/apps/member/signals.py b/apps/member/signals.py index 5debce4b..3361f9de 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -def save_user_note(instance, created, raw, **_kwargs): +def save_user_profile(instance, created, raw, **_kwargs): """ Hook to create and save a profile when an user is updated if it is not registered with the signup form """ @@ -11,5 +11,5 @@ def save_user_note(instance, created, raw, **_kwargs): if created: from .models import Profile - Profile.objects.get_or_create(user=instance) + #Profile.objects.get_or_create(user=instance) instance.profile.save() From b23237f669cc9dc93b007548e7083f07600ddeaf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 27 Feb 2020 16:26:07 +0100 Subject: [PATCH 14/14] Uncomment required line --- apps/member/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/member/signals.py b/apps/member/signals.py index 3361f9de..b17b3ae8 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -11,5 +11,5 @@ def save_user_profile(instance, created, raw, **_kwargs): if created: from .models import Profile - #Profile.objects.get_or_create(user=instance) + Profile.objects.get_or_create(user=instance) instance.profile.save()