From 57709591f34f2aebe4aed8bdd3d92d10895d8a1c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:51:51 -0700 Subject: [PATCH] Change: default-enable audit log --- docs/configuration.md | 8 ++---- src/documents/admin.py | 4 +-- .../management/commands/document_exporter.py | 8 ++++++ src/documents/models.py | 4 +-- src/documents/tasks.py | 4 +-- .../tests/test_management_exporter.py | 28 +++++++++++++++++++ src/documents/views.py | 6 ++-- src/paperless/checks.py | 10 ++----- src/paperless/settings.py | 4 +-- src/paperless/tests/test_checks.py | 7 ++--- src/setup.cfg | 4 +-- 11 files changed, 57 insertions(+), 30 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 981d27b27..725d7c522 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1309,13 +1309,11 @@ assigns or creates tags if a properly formatted barcode is detected. ## Audit Trail -#### [`PAPERLESS_AUDIT_LOG_ENABLED=`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED} +#### [`PAPERLESS_AUDIT_LOG_DISABLED=`](#PAPERLESS_AUDIT_LOG_DISABLED) {#PAPERLESS_AUDIT_LOG_DISABLED} -: Enables an audit trail for documents, document types, correspondents, and tags. Log entries can be viewed in the Django backend only. +: Disables the audit trail for documents, document types, correspondents, and tags. - !!! warning - - Once enabled cannot be disabled + Defaults to false. ## Collate Double-Sided Documents {#collate} diff --git a/src/documents/admin.py b/src/documents/admin.py index 5df235618..1eb9ba65b 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -15,7 +15,7 @@ from documents.models import ShareLink from documents.models import StoragePath from documents.models import Tag -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: from auditlog.admin import LogEntryAdmin from auditlog.models import LogEntry @@ -197,7 +197,7 @@ admin.site.register(ShareLink, ShareLinksAdmin) admin.site.register(CustomField, CustomFieldsAdmin) admin.site.register(CustomFieldInstance, CustomFieldInstancesAdmin) -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: class LogEntryAUDIT(LogEntryAdmin): def has_delete_permission(self, request, obj=None): diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py index faa42ba31..b19a5d376 100644 --- a/src/documents/management/commands/document_exporter.py +++ b/src/documents/management/commands/document_exporter.py @@ -9,6 +9,9 @@ from typing import Optional import tqdm from django.conf import settings + +if not settings.AUDIT_LOG_DISABLED: + from auditlog.models import LogEntry from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.auth.models import User @@ -307,6 +310,11 @@ class Command(BaseCommand): serializers.serialize("json", ApplicationConfiguration.objects.all()), ) + if not settings.AUDIT_LOG_DISABLED: + manifest += json.loads( + serializers.serialize("json", LogEntry.objects.all()), + ) + # These are treated specially and included in the per-document manifest # if that setting is enabled. Otherwise, they are just exported to the bulk # manifest diff --git a/src/documents/models.py b/src/documents/models.py index 8e7a16a60..d46c11dd9 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -20,7 +20,7 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from multiselectfield import MultiSelectField -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: from auditlog.registry import auditlog from documents.data_models import DocumentSource @@ -881,7 +881,7 @@ class CustomFieldInstance(models.Model): raise NotImplementedError(self.field.data_type) -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: auditlog.register(Document, m2m_fields={"tags"}) auditlog.register(Correspondent) auditlog.register(Tag) diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 0ab55ac45..c011a4935 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -43,7 +43,7 @@ from documents.plugins.helpers import ProgressStatusOptions from documents.sanity_checker import SanityCheckFailedException from documents.signals import document_updated -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: import json from auditlog.models import LogEntry @@ -275,7 +275,7 @@ def update_document_archive_file(document_id): archive_filename=document.archive_filename, ) newDocument = Document.objects.get(pk=document.pk) - if settings.AUDIT_LOG_ENABLED: + if not settings.AUDIT_LOG_DISABLED: LogEntry.objects.log_create( instance=oldDocument, changes=json.dumps( diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py index 226b89694..1a79f9949 100644 --- a/src/documents/tests/test_management_exporter.py +++ b/src/documents/tests/test_management_exporter.py @@ -7,6 +7,7 @@ from pathlib import Path from unittest import mock from zipfile import ZipFile +from auditlog.models import LogEntry from django.contrib.auth.models import Group from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType @@ -122,6 +123,19 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): self.workflow.actions.add(self.action) self.workflow.save() + LogEntry.objects.log_create( + instance=self.dt1, + changes=json.dumps( + { + "name": [ + self.dt1.name, + "New name", + ], + }, + ), + action=LogEntry.Action.UPDATE, + ) + super().setUp() def _get_document_from_manifest(self, manifest, id): @@ -780,3 +794,17 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase): self.assertEqual(ContentType.objects.count(), num_content_type_objects) self.assertEqual(Permission.objects.count(), num_permission_objects + 1) + + def test_exporter_with_auditlog_disabled(self): + shutil.rmtree(os.path.join(self.dirs.media_dir, "documents")) + shutil.copytree( + os.path.join(os.path.dirname(__file__), "samples", "documents"), + os.path.join(self.dirs.media_dir, "documents"), + ) + + with override_settings( + AUDIT_LOG_DISABLED=True, + ): + manifest = self._do_export(use_filename_format=True) + for obj in manifest: + self.assertNotEqual(obj["model"], "auditlog.logentry") diff --git a/src/documents/views.py b/src/documents/views.py index 3fcc54023..e700b39eb 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -153,7 +153,7 @@ from paperless.config import GeneralConfig from paperless.db import GnuPG from paperless.views import StandardPagination -if settings.AUDIT_LOG_ENABLED: +if not settings.AUDIT_LOG_DISABLED: from auditlog.models import LogEntry logger = logging.getLogger("paperless.api") @@ -636,7 +636,7 @@ class DocumentViewSet( c.save() # If audit log is enabled make an entry in the log # about this note change - if settings.AUDIT_LOG_ENABLED: + if not settings.AUDIT_LOG_DISABLED: LogEntry.objects.log_create( instance=doc, changes=json.dumps( @@ -671,7 +671,7 @@ class DocumentViewSet( return HttpResponseForbidden("Insufficient permissions to delete notes") note = Note.objects.get(id=int(request.GET.get("id"))) - if settings.AUDIT_LOG_ENABLED: + if not settings.AUDIT_LOG_DISABLED: LogEntry.objects.log_create( instance=doc, changes=json.dumps( diff --git a/src/paperless/checks.py b/src/paperless/checks.py index 0bfdce2cd..baf537138 100644 --- a/src/paperless/checks.py +++ b/src/paperless/checks.py @@ -5,7 +5,6 @@ import shutil import stat from django.conf import settings -from django.core.checks import Critical from django.core.checks import Error from django.core.checks import Warning from django.core.checks import register @@ -205,13 +204,10 @@ def audit_log_check(app_configs, **kwargs): all_tables = db_conn.introspection.table_names() result = [] - if ("auditlog_logentry" in all_tables) and not (settings.AUDIT_LOG_ENABLED): + if ("auditlog_logentry" in all_tables) or (settings.AUDIT_LOG_DISABLED): result.append( - Critical( - ( - "auditlog table was found but PAPERLESS_AUDIT_LOG_ENABLED" - " is not active. This setting cannot be disabled after enabling" - ), + Warning( + ("auditlog table was found but AUDIT_LOG_DISABLED is active."), ), ) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index a583ef406..2e2472701 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -1045,8 +1045,8 @@ TIKA_GOTENBERG_ENDPOINT = os.getenv( if TIKA_ENABLED: INSTALLED_APPS.append("paperless_tika.apps.PaperlessTikaConfig") -AUDIT_LOG_ENABLED = __get_boolean("PAPERLESS_AUDIT_LOG_ENABLED", "NO") -if AUDIT_LOG_ENABLED: +AUDIT_LOG_DISABLED = __get_boolean("PAPERLESS_AUDIT_LOG_DISABLED", "NO") +if not AUDIT_LOG_DISABLED: INSTALLED_APPS.append("auditlog") MIDDLEWARE.append("auditlog.middleware.AuditlogMiddleware") diff --git a/src/paperless/tests/test_checks.py b/src/paperless/tests/test_checks.py index a6879cdbf..61a637322 100644 --- a/src/paperless/tests/test_checks.py +++ b/src/paperless/tests/test_checks.py @@ -247,7 +247,7 @@ class TestAuditLogChecks(TestCase): """ introspect_mock = mock.MagicMock() introspect_mock.introspection.table_names.return_value = ["auditlog_logentry"] - with override_settings(AUDIT_LOG_ENABLED=False): + with override_settings(AUDIT_LOG_DISABLED=True): with mock.patch.dict( "paperless.checks.connections", {"default": introspect_mock}, @@ -259,9 +259,6 @@ class TestAuditLogChecks(TestCase): msg = msgs[0] self.assertIn( - ( - "auditlog table was found but PAPERLESS_AUDIT_LOG_ENABLED" - " is not active." - ), + ("auditlog table was found but AUDIT_LOG_DISABLED is active."), msg.msg, ) diff --git a/src/setup.cfg b/src/setup.cfg index 1877cb16e..eda3daae2 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -17,8 +17,8 @@ omit = [coverage:report] exclude_also = - if settings.AUDIT_LOG_ENABLED: - if AUDIT_LOG_ENABLED: + if settings.AUDIT_LOG_DISABLED: + if AUDIT_LOG_DISABLED: if TYPE_CHECKING: [mypy]