Change: default-enable audit log
This commit is contained in:
parent
00b04c2e86
commit
57709591f3
@ -1309,13 +1309,11 @@ assigns or creates tags if a properly formatted barcode is detected.
|
|||||||
|
|
||||||
## Audit Trail
|
## Audit Trail
|
||||||
|
|
||||||
#### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED}
|
#### [`PAPERLESS_AUDIT_LOG_DISABLED=<bool>`](#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
|
Defaults to false.
|
||||||
|
|
||||||
Once enabled cannot be disabled
|
|
||||||
|
|
||||||
## Collate Double-Sided Documents {#collate}
|
## Collate Double-Sided Documents {#collate}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from documents.models import ShareLink
|
|||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
from auditlog.admin import LogEntryAdmin
|
from auditlog.admin import LogEntryAdmin
|
||||||
from auditlog.models import LogEntry
|
from auditlog.models import LogEntry
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ admin.site.register(ShareLink, ShareLinksAdmin)
|
|||||||
admin.site.register(CustomField, CustomFieldsAdmin)
|
admin.site.register(CustomField, CustomFieldsAdmin)
|
||||||
admin.site.register(CustomFieldInstance, CustomFieldInstancesAdmin)
|
admin.site.register(CustomFieldInstance, CustomFieldInstancesAdmin)
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
|
|
||||||
class LogEntryAUDIT(LogEntryAdmin):
|
class LogEntryAUDIT(LogEntryAdmin):
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
@ -9,6 +9,9 @@ from typing import Optional
|
|||||||
|
|
||||||
import tqdm
|
import tqdm
|
||||||
from django.conf import settings
|
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 Group
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@ -307,6 +310,11 @@ class Command(BaseCommand):
|
|||||||
serializers.serialize("json", ApplicationConfiguration.objects.all()),
|
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
|
# These are treated specially and included in the per-document manifest
|
||||||
# if that setting is enabled. Otherwise, they are just exported to the bulk
|
# if that setting is enabled. Otherwise, they are just exported to the bulk
|
||||||
# manifest
|
# manifest
|
||||||
|
@ -20,7 +20,7 @@ from django.utils import timezone
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from multiselectfield import MultiSelectField
|
from multiselectfield import MultiSelectField
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
from auditlog.registry import auditlog
|
from auditlog.registry import auditlog
|
||||||
|
|
||||||
from documents.data_models import DocumentSource
|
from documents.data_models import DocumentSource
|
||||||
@ -881,7 +881,7 @@ class CustomFieldInstance(models.Model):
|
|||||||
raise NotImplementedError(self.field.data_type)
|
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(Document, m2m_fields={"tags"})
|
||||||
auditlog.register(Correspondent)
|
auditlog.register(Correspondent)
|
||||||
auditlog.register(Tag)
|
auditlog.register(Tag)
|
||||||
|
@ -43,7 +43,7 @@ from documents.plugins.helpers import ProgressStatusOptions
|
|||||||
from documents.sanity_checker import SanityCheckFailedException
|
from documents.sanity_checker import SanityCheckFailedException
|
||||||
from documents.signals import document_updated
|
from documents.signals import document_updated
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from auditlog.models import LogEntry
|
from auditlog.models import LogEntry
|
||||||
@ -275,7 +275,7 @@ def update_document_archive_file(document_id):
|
|||||||
archive_filename=document.archive_filename,
|
archive_filename=document.archive_filename,
|
||||||
)
|
)
|
||||||
newDocument = Document.objects.get(pk=document.pk)
|
newDocument = Document.objects.get(pk=document.pk)
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
LogEntry.objects.log_create(
|
LogEntry.objects.log_create(
|
||||||
instance=oldDocument,
|
instance=oldDocument,
|
||||||
changes=json.dumps(
|
changes=json.dumps(
|
||||||
|
@ -7,6 +7,7 @@ from pathlib import Path
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
from auditlog.models import LogEntry
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
@ -122,6 +123,19 @@ class TestExportImport(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
|
|||||||
self.workflow.actions.add(self.action)
|
self.workflow.actions.add(self.action)
|
||||||
self.workflow.save()
|
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()
|
super().setUp()
|
||||||
|
|
||||||
def _get_document_from_manifest(self, manifest, id):
|
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(ContentType.objects.count(), num_content_type_objects)
|
||||||
self.assertEqual(Permission.objects.count(), num_permission_objects + 1)
|
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")
|
||||||
|
@ -153,7 +153,7 @@ from paperless.config import GeneralConfig
|
|||||||
from paperless.db import GnuPG
|
from paperless.db import GnuPG
|
||||||
from paperless.views import StandardPagination
|
from paperless.views import StandardPagination
|
||||||
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
from auditlog.models import LogEntry
|
from auditlog.models import LogEntry
|
||||||
|
|
||||||
logger = logging.getLogger("paperless.api")
|
logger = logging.getLogger("paperless.api")
|
||||||
@ -636,7 +636,7 @@ class DocumentViewSet(
|
|||||||
c.save()
|
c.save()
|
||||||
# If audit log is enabled make an entry in the log
|
# If audit log is enabled make an entry in the log
|
||||||
# about this note change
|
# about this note change
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if not settings.AUDIT_LOG_DISABLED:
|
||||||
LogEntry.objects.log_create(
|
LogEntry.objects.log_create(
|
||||||
instance=doc,
|
instance=doc,
|
||||||
changes=json.dumps(
|
changes=json.dumps(
|
||||||
@ -671,7 +671,7 @@ class DocumentViewSet(
|
|||||||
return HttpResponseForbidden("Insufficient permissions to delete notes")
|
return HttpResponseForbidden("Insufficient permissions to delete notes")
|
||||||
|
|
||||||
note = Note.objects.get(id=int(request.GET.get("id")))
|
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(
|
LogEntry.objects.log_create(
|
||||||
instance=doc,
|
instance=doc,
|
||||||
changes=json.dumps(
|
changes=json.dumps(
|
||||||
|
@ -5,7 +5,6 @@ import shutil
|
|||||||
import stat
|
import stat
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.checks import Critical
|
|
||||||
from django.core.checks import Error
|
from django.core.checks import Error
|
||||||
from django.core.checks import Warning
|
from django.core.checks import Warning
|
||||||
from django.core.checks import register
|
from django.core.checks import register
|
||||||
@ -205,13 +204,10 @@ def audit_log_check(app_configs, **kwargs):
|
|||||||
all_tables = db_conn.introspection.table_names()
|
all_tables = db_conn.introspection.table_names()
|
||||||
result = []
|
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(
|
result.append(
|
||||||
Critical(
|
Warning(
|
||||||
(
|
("auditlog table was found but AUDIT_LOG_DISABLED is active."),
|
||||||
"auditlog table was found but PAPERLESS_AUDIT_LOG_ENABLED"
|
|
||||||
" is not active. This setting cannot be disabled after enabling"
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1045,8 +1045,8 @@ TIKA_GOTENBERG_ENDPOINT = os.getenv(
|
|||||||
if TIKA_ENABLED:
|
if TIKA_ENABLED:
|
||||||
INSTALLED_APPS.append("paperless_tika.apps.PaperlessTikaConfig")
|
INSTALLED_APPS.append("paperless_tika.apps.PaperlessTikaConfig")
|
||||||
|
|
||||||
AUDIT_LOG_ENABLED = __get_boolean("PAPERLESS_AUDIT_LOG_ENABLED", "NO")
|
AUDIT_LOG_DISABLED = __get_boolean("PAPERLESS_AUDIT_LOG_DISABLED", "NO")
|
||||||
if AUDIT_LOG_ENABLED:
|
if not AUDIT_LOG_DISABLED:
|
||||||
INSTALLED_APPS.append("auditlog")
|
INSTALLED_APPS.append("auditlog")
|
||||||
MIDDLEWARE.append("auditlog.middleware.AuditlogMiddleware")
|
MIDDLEWARE.append("auditlog.middleware.AuditlogMiddleware")
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ class TestAuditLogChecks(TestCase):
|
|||||||
"""
|
"""
|
||||||
introspect_mock = mock.MagicMock()
|
introspect_mock = mock.MagicMock()
|
||||||
introspect_mock.introspection.table_names.return_value = ["auditlog_logentry"]
|
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(
|
with mock.patch.dict(
|
||||||
"paperless.checks.connections",
|
"paperless.checks.connections",
|
||||||
{"default": introspect_mock},
|
{"default": introspect_mock},
|
||||||
@ -259,9 +259,6 @@ class TestAuditLogChecks(TestCase):
|
|||||||
msg = msgs[0]
|
msg = msgs[0]
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
(
|
("auditlog table was found but AUDIT_LOG_DISABLED is active."),
|
||||||
"auditlog table was found but PAPERLESS_AUDIT_LOG_ENABLED"
|
|
||||||
" is not active."
|
|
||||||
),
|
|
||||||
msg.msg,
|
msg.msg,
|
||||||
)
|
)
|
||||||
|
@ -17,8 +17,8 @@ omit =
|
|||||||
|
|
||||||
[coverage:report]
|
[coverage:report]
|
||||||
exclude_also =
|
exclude_also =
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
if settings.AUDIT_LOG_DISABLED:
|
||||||
if AUDIT_LOG_ENABLED:
|
if AUDIT_LOG_DISABLED:
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user