From fc5f4caf52b50d674c012d0cf6f4e4eb33caadd2 Mon Sep 17 00:00:00 2001 From: Eli Keimig Date: Sat, 1 Feb 2020 22:15:58 -0500 Subject: [PATCH 1/2] added username persistence and admin read-only functionality --- README.md | 3 +++ rest_framework_tracking/admin.py | 7 +++++++ rest_framework_tracking/base_mixins.py | 2 ++ rest_framework_tracking/base_models.py | 4 ++++ .../migrations/0008_auto_20200201_2048.py | 21 +++++++++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 rest_framework_tracking/migrations/0008_auto_20200201_2048.py diff --git a/README.md b/README.md index a67da88..7fd1603 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ drf-tracking provides a Django model and DRF view mixin that work together to lo Model field name | Description | Model field type ------------------|-------------|----------------- `user` | User if authenticated, None if not | Foreign Key +`username_persistent` | Static field that persists the username even if the User model object is deleted | CharField `requested_at` | Date-time that the request was made | DateTimeField `response_ms` | Number of milliseconds spent in view code | PositiveIntegerField `path` | Target URI of the request, e.g., `"/api/"` | CharField @@ -153,6 +154,8 @@ class LoggingView(LoggingMixin, generics.CreateModelMixin, generics.GenericAPIVi sensitive_fields = {'my_secret_key', 'my_secret_recipe'} ``` +By default drf-tracking allows API request log entries to be modified from Django admin. This can present a data integrity issue in production environments. In order to change this behavior, you can set `DRF_TRACKING_ADMIN_LOG_READONLY` to `True` in your `settings.py` file. + ## Testing Install testing requirements. diff --git a/rest_framework_tracking/admin.py b/rest_framework_tracking/admin.py index cd38f81..052dd0a 100644 --- a/rest_framework_tracking/admin.py +++ b/rest_framework_tracking/admin.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.contrib import admin from .models import APIRequestLog @@ -12,5 +13,11 @@ class APIRequestLogAdmin(admin.ModelAdmin): search_fields = ('path', 'user__email',) raw_id_fields = ('user', ) + if getattr(settings, 'DRF_TRACKING_ADMIN_LOG_READONLY', False): + readonly_fields = ('user', 'username_persistent', 'requested_at', + 'response_ms', 'path', 'view', 'view_method', + 'remote_addr', 'host', 'method', 'query_params', + 'data', 'response', 'errors', 'status_code') + admin.site.register(APIRequestLog, APIRequestLogAdmin) diff --git a/rest_framework_tracking/base_mixins.py b/rest_framework_tracking/base_mixins.py index f00ef37..8b167be 100644 --- a/rest_framework_tracking/base_mixins.py +++ b/rest_framework_tracking/base_mixins.py @@ -68,6 +68,8 @@ def finalize_response(self, request, response, *args, **kwargs): 'method': request.method, 'query_params': self._clean_data(request.query_params.dict()), 'user': self._get_user(request), + 'username_persistent': + self._get_user(request).username if self._get_user(request) else 'Anonymous', 'response_ms': self._get_response_ms(), 'response': self._clean_data(rendered_content), 'status_code': response.status_code, diff --git a/rest_framework_tracking/base_models.py b/rest_framework_tracking/base_models.py index 56eed69..f7a385e 100644 --- a/rest_framework_tracking/base_models.py +++ b/rest_framework_tracking/base_models.py @@ -14,6 +14,10 @@ class BaseAPIRequestLog(models.Model): null=True, blank=True, ) + username_persistent = models.CharField( + max_length=getattr(settings, 'DRF_TRACKING_USERNAME_LENGTH', 200), + db_index=True, + ) requested_at = models.DateTimeField(db_index=True) response_ms = models.PositiveIntegerField(default=0) path = models.CharField( diff --git a/rest_framework_tracking/migrations/0008_auto_20200201_2048.py b/rest_framework_tracking/migrations/0008_auto_20200201_2048.py new file mode 100644 index 0000000..ed7cd18 --- /dev/null +++ b/rest_framework_tracking/migrations/0008_auto_20200201_2048.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 3.0.2 on 2020-02-01 20:48 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rest_framework_tracking', '0007_merge_20180419_1646'), + ] + + operations = [ + migrations.AddField( + model_name='apirequestlog', + name='username_persistent', + field=models.CharField(db_index=True, default='', max_length=200), + preserve_default=False, + ), + ] From cddae0c536c46530ab7db93b27a66948a8c3bc55 Mon Sep 17 00:00:00 2001 From: Eli Keimig Date: Sat, 1 Feb 2020 22:24:55 -0500 Subject: [PATCH 2/2] adjust new field for legacy support --- rest_framework_tracking/base_models.py | 3 ++- .../migrations/0008_auto_20200201_2048.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rest_framework_tracking/base_models.py b/rest_framework_tracking/base_models.py index f7a385e..9bbefbd 100644 --- a/rest_framework_tracking/base_models.py +++ b/rest_framework_tracking/base_models.py @@ -16,7 +16,8 @@ class BaseAPIRequestLog(models.Model): ) username_persistent = models.CharField( max_length=getattr(settings, 'DRF_TRACKING_USERNAME_LENGTH', 200), - db_index=True, + null=True, + blank=True, ) requested_at = models.DateTimeField(db_index=True) response_ms = models.PositiveIntegerField(default=0) diff --git a/rest_framework_tracking/migrations/0008_auto_20200201_2048.py b/rest_framework_tracking/migrations/0008_auto_20200201_2048.py index ed7cd18..a0a644d 100644 --- a/rest_framework_tracking/migrations/0008_auto_20200201_2048.py +++ b/rest_framework_tracking/migrations/0008_auto_20200201_2048.py @@ -15,7 +15,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='apirequestlog', name='username_persistent', - field=models.CharField(db_index=True, default='', max_length=200), - preserve_default=False, + field=models.CharField( + blank=True, + default='', + max_length=200, + null=True + ) ), ]