diff options
-rw-r--r-- | docs/fr/source/administrateur-applicatif.rst | 3 | ||||
-rw-r--r-- | ishtar_common/admin.py | 14 | ||||
-rw-r--r-- | ishtar_common/migrations/0233_gdprlog_gdprperson.py | 9 | ||||
-rw-r--r-- | ishtar_common/models.py | 21 | ||||
-rw-r--r-- | ishtar_common/tests.py | 6 | ||||
-rw-r--r-- | requirements.txt | 2 |
6 files changed, 39 insertions, 16 deletions
diff --git a/docs/fr/source/administrateur-applicatif.rst b/docs/fr/source/administrateur-applicatif.rst index f3e94047f..3336d0c9b 100644 --- a/docs/fr/source/administrateur-applicatif.rst +++ b/docs/fr/source/administrateur-applicatif.rst @@ -301,6 +301,7 @@ La journalisation consiste à assurer la traçabilité des actions opérées sur - l'identifiant utilisateur qui réalise le traitement, - la date et l’heure de l’accès, - l'adresse IP de connexion, +- l'information si cette IP est « routable » (i.e l'IP est routable si la connexion vient d'Internet sinon la connexion vient du réseau interne au serveur), - le type de traitement, - des liens vers les personnes concernées par le traitement. @@ -327,7 +328,7 @@ Ces données sont accessibles en consultation uniquement (la suppression et la m .. note:: Si des utilisateurs disposent d'un statut « super utilisateur » mais ne sont pas administrateur RGPD, il est nécessaire de leur retirer ce statut pour leur donner le groupe « administrateur technique ». - +Lors du contrôle de ces données, une attention particulière sera portée sur les lignes ne disposant pas de l'information de l'utilisateur et/ou de l'adresse IP associée et/ou si l'adresse IP est non routable. Il peut s'agir d'entrées relatives à, dans le cas le plus courant, un script de maintenance, à un dysfonctionnement ou alors, dans le pire des cas, d'une compromission de la base de données. Remontez l'information au plus vite au référent administration système. .. TODO (ou pas) diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 803b5766a..62faf3b77 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -191,11 +191,16 @@ def export_as_csv_action( writer.writerow(modeladmin.CSV_HEADER) else: writer.writerow(list(field_names)) + boolean_fields = [] + if hasattr(modeladmin, "CSV_FIELDS_BOOLEAN"): + boolean_fields = modeladmin.CSV_FIELDS_BOOLEAN for obj in queryset.order_by("pk"): row = [] for field in field_names: value = getattr(obj, field) - if hasattr(value, "txt_idx"): + if field in boolean_fields: + value = str(_("True")) if value else str(_("False")) + elif hasattr(value, "txt_idx"): value = getattr(value, "txt_idx") elif hasattr(value, "slug"): value = getattr(value, "txt_idx") @@ -708,14 +713,15 @@ admin_site.register(models.Person, PersonAdmin) @admin.register(models.GDPRLog, site=admin_site) class GDPRLogAdmin(admin.ModelAdmin): - list_display = ("user", "date", "ip", "activity") + list_display = ("user", "date", "ip", "routable_ip", "activity") list_filter = ("activity",) search_fields = ("user__username",) actions = [ export_as_csv_action(exclude=("id",)), ] - CSV_HEADER = (_("Date"), _("User"), _("IP"), _("Activity"), _("Persons")) - CSV_FIELDS = ("date", "user", "ip", "activity_lbl", "persons_lbl") + CSV_HEADER = (_("Date"), _("User"), _("IP"), _("Routable IP"), _("Activity"), _("Persons")) + CSV_FIELDS = ("date", "user", "ip", "routable_ip", "activity_lbl", "persons_lbl") + CSV_FIELDS_BOOLEAN = ("routable_ip",) def has_add_permission(self, request, obj=None): return False diff --git a/ishtar_common/migrations/0233_gdprlog_gdprperson.py b/ishtar_common/migrations/0233_gdprlog_gdprperson.py index 374134fce..10ddc9170 100644 --- a/ishtar_common/migrations/0233_gdprlog_gdprperson.py +++ b/ishtar_common/migrations/0233_gdprlog_gdprperson.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.24 on 2023-11-16 15:48 +# Generated by Django 2.2.24 on 2023-11-17 12:04 import datetime from django.conf import settings @@ -19,7 +19,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('raw_name', models.CharField(default='-', max_length=300, verbose_name='Raw name')), - ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.Person', verbose_name='Person')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='gdpr_person', to='ishtar_common.Person', verbose_name='Person')), ], options={ 'verbose_name': 'GDPR - Person', @@ -31,10 +31,11 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateTimeField(default=datetime.datetime.now, verbose_name='Date')), - ('ip', models.GenericIPAddressField(verbose_name='IP')), + ('ip', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP')), + ('routable_ip', models.BooleanField(default=False, verbose_name='Routable IP')), ('activity', models.CharField(choices=[('DC', 'Directory consultation'), ('DE', 'Directory export'), ('PV', "Viewing a person's notice"), ('PE', "Exporting a person's notice"), ('PC', 'Person creation'), ('PM', 'Person modification'), ('PD', 'Person deletion')], max_length=2, verbose_name='Activity')), ('persons', models.ManyToManyField(blank=True, to='ishtar_common.GDPRPerson', verbose_name='Persons')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='User')), ], options={ 'verbose_name': 'GDPR - Log', diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 8cb59be3e..a7f715516 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -20,6 +20,7 @@ """ Models description """ +from ipware import get_client_ip import sys from bs4 import BeautifulSoup @@ -3230,9 +3231,11 @@ class GDPRPerson(models.Model): class GDPRLog(models.Model): - user = models.ForeignKey(User, verbose_name=_("User"), on_delete=models.PROTECT) + user = models.ForeignKey(User, verbose_name=_("User"), on_delete=models.PROTECT, blank=True, + null=True) date = models.DateTimeField(verbose_name=_("Date"), default=datetime.datetime.now) - ip = models.GenericIPAddressField(verbose_name=_("IP")) + ip = models.GenericIPAddressField(verbose_name=_("IP"), blank=True, null=True) + routable_ip = models.BooleanField(verbose_name=_("Routable IP"), default=False) activity = models.CharField(_("Activity"), max_length=2, choices=GDPR_ACTIVITY) persons = models.ManyToManyField(GDPRPerson, verbose_name=_("Persons"), blank=True) @@ -3260,8 +3263,18 @@ class GDPRLog(models.Model): return f"{self.user.username} - {self.date} - {self.activity_lbl}" @classmethod - def create_log(cls, user_id, ip, activity, person_query): - log = cls.objects.create(user_id=user_id, ip=ip, activity=activity) + def create_log(cls, request, activity, person_query): + if not request.user: + # log creation is for logged user should be a script, a bug or a hacker... + user_id = None + else: + user_id = request.user.id + client_ip, routable_ip = get_client_ip(request) + cls._create_log(user_id, client_ip, routable_ip, activity, person_query) + + @classmethod + def _create_log(cls, user_id, ip, routable_ip, activity, person_query): + log = cls.objects.create(user_id=user_id, ip=ip, routable_ip=routable_ip, activity=activity) person_query = person_query.exclude(raw_name__isnull=True).exclude(raw_name="") # create all missing GDPRPerson diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index e8821f18c..ed359db72 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -2082,7 +2082,7 @@ class GDPRTest(TestCase): nb_person = models.GDPRPerson.objects.count() q = models.Person.objects.filter(pk__in=(self.person_1.pk, self.person_2.pk)) - models.GDPRLog.create_log(self.user.pk, "127.0.0.1", "DC", q) + models.GDPRLog._create_log(self.user.pk, "127.0.0.1", False, "DC", q) self.assertEqual(models.GDPRLog.objects.count(), nb + 1) self.assertEqual(models.GDPRPerson.objects.count(), nb_person + 2) last_log = models.GDPRLog.objects.order_by("-pk").all()[0] @@ -2091,7 +2091,7 @@ class GDPRTest(TestCase): nb = models.GDPRLog.objects.count() nb_person = models.GDPRPerson.objects.count() q = models.Person.objects.filter(pk__in=(self.person_1.pk, self.person_3.pk)) - models.GDPRLog.create_log(self.user.pk, "127.0.1.1", "DE", q) + models.GDPRLog._create_log(self.user.pk, "127.0.1.1", False, "DE", q) self.assertEqual(models.GDPRLog.objects.count(), nb + 1) self.assertEqual(models.GDPRPerson.objects.count(), nb_person + 1) last_log = models.GDPRLog.objects.order_by("-pk").all()[0] @@ -2104,7 +2104,7 @@ class GDPRTest(TestCase): models.Person.objects.bulk_create(persons) n = datetime.datetime.now() q = models.Person.objects - models.GDPRLog.create_log(self.user.pk, "127.0.0.1", "DE", q) + models.GDPRLog._create_log(self.user.pk, "127.0.0.1", False, "DE", q) self.assertTrue((datetime.datetime.now() - n).seconds < 3) diff --git a/requirements.txt b/requirements.txt index ca92ba526..604c879ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -65,4 +65,6 @@ django-extensions==3.0.3 # django-debug-toolbar==3.2.4 pandas==1.1.5 +django-ipware==3.0.0 + django-axes==5.4.3 |