Merge upstream 2.1.1 and partial 2.2.0

This commit is contained in:
Aleh Rymašeŭski 2023-07-13 14:42:31 +00:00
commit c65a0b23c4
9 changed files with 110 additions and 38 deletions

View file

@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.6.0
hooks:
- id: black
language_version: python3.8
@ -9,7 +9,7 @@ repos:
- "--target-version"
- "py37"
- repo: https://github.com/PyCQA/flake8
rev: "4.0.1"
rev: "5.0.4"
hooks:
- id: flake8
args: ["--max-line-length", "110"]
@ -18,7 +18,12 @@ repos:
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v2.34.0
rev: v2.37.3
hooks:
- id: pyupgrade
args: [--py37-plus]
args: [--py37-plus]
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.8.0
hooks:
- id: django-upgrade
args: [--target-version, "3.2"]

View file

@ -1,5 +1,21 @@
# Changes
#### Fixes
- fix: Display `created` timestamp in server timezone ([#404](https://github.com/jazzband/django-auditlog/pull/404))
- fix: Handle port in `remote_addr` ([#417](https://github.com/jazzband/django-auditlog/pull/417))
## 2.1.1 (2022-07-27)
#### Improvements
- feat: Display the diff for deleted objects in the admin ([#396](https://github.com/jazzband/django-auditlog/pull/396))
- Django: Confirm Django 4.1 support ([#406](https://github.com/jazzband/django-auditlog/pull/406))
#### Fixes
- fix: Pin `python-dateutil` to 2.7.0 or higher for compatibility with Python 3.10 ([#401](https://github.com/jazzband/django-auditlog/pull/401))
## 2.1.0 (2022-06-27)
#### Improvements

View file

@ -32,8 +32,10 @@ If you have great ideas for Auditlog, or if you like to improve something, feel
Releases
--------
1. Make sure all tests on `master` are green.
2. Create a new branch `vX.Y.Z` from master for that specific release.
3. Bump versions in `setup.py` and `docs/source/conf.py` (docs have 2 places where the versions need to be changed!)
4. Pull request `vX.Y.Z` -> `master`.
5. Pull request `master` -> `stable`. This merge triggers the deploy to pypi.
1. Make sure all tests on `master` are green
2. Create a new branch `vX.Y.Z` from master for that specific release
3. Update the CHANGELOG release date
4. Pull request `vX.Y.Z` -> `master`
5. As a project lead, once the PR is merged, create and push a tag `vX.Y.Z`: this will trigger the release build and a notification will be sent from Jazzband of the availability of two packages (tgz and wheel)
6. Test the install
7. Publish the release to PyPI

View file

@ -12,13 +12,18 @@ class AuditlogMiddleware:
def __init__(self, get_response=None):
self.get_response = get_response
def __call__(self, request):
if request.META.get("HTTP_X_FORWARDED_FOR"):
@staticmethod
def _get_remote_addr(request):
if request.headers.get("X-Forwarded-For"):
# In case of proxy, set 'original' address
remote_addr = request.META.get("HTTP_X_FORWARDED_FOR").split(",")[0]
remote_addr = request.headers.get("X-Forwarded-For").split(",")[0]
# Remove port number from remote_addr
return remote_addr.split(":")[0]
else:
remote_addr = request.META.get("REMOTE_ADDR")
return request.META.get("REMOTE_ADDR")
def __call__(self, request):
remote_addr = self._get_remote_addr(request)
if hasattr(request, "user") and request.user.is_authenticated:
context = set_actor(actor=request.user, remote_addr=remote_addr)

View file

@ -64,8 +64,6 @@ class LogEntryAdminMixin:
msg_short.short_description = "Changes"
def msg(self, obj):
if obj.action == LogEntry.Action.DELETE:
return "" # delete
changes = json.loads(obj.changes)
atom_changes = {}

View file

@ -4,6 +4,7 @@ import json
import warnings
from unittest import mock
import freezegun
from dateutil.tz import gettz
from django.apps import apps
from django.conf import settings
@ -436,6 +437,19 @@ class MiddlewareTest(TestCase):
self.assert_no_listeners()
def test_get_remote_addr(self):
tests = [ # (headers, expected_remote_addr)
({}, "127.0.0.1"),
({"HTTP_X_FORWARDED_FOR": "127.0.0.2"}, "127.0.0.2"),
({"HTTP_X_FORWARDED_FOR": "127.0.0.3:1234"}, "127.0.0.3"),
]
for headers, expected_remote_addr in tests:
with self.subTest(headers=headers):
request = self.factory.get("/", **headers)
self.assertEqual(
self.middleware._get_remote_addr(request), expected_remote_addr
)
class SimpleIncludeModelTest(TestCase):
"""Log only changes in include_fields"""
@ -1233,31 +1247,40 @@ class PostgresArrayFieldModelTest(TestCase):
class AdminPanelTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.username = "test_admin"
cls.password = User.objects.make_random_password()
cls.user, created = User.objects.get_or_create(username=cls.username)
cls.user.set_password(cls.password)
cls.user.is_staff = True
cls.user.is_superuser = True
cls.user.is_active = True
cls.user.save()
cls.obj = SimpleModel.objects.create(text="For admin logentry test")
def setUp(self):
self.user = User.objects.create_user(
username="test_admin", is_staff=True, is_superuser=True, is_active=True
)
self.site = AdminSite()
self.admin = LogEntryAdmin(LogEntry, self.site)
with freezegun.freeze_time("2022-08-01 12:00:00Z"):
self.obj = SimpleModel.objects.create(text="For admin logentry test")
def test_auditlog_admin(self):
self.client.login(username=self.username, password=self.password)
self.client.force_login(self.user)
log_pk = self.obj.history.latest().pk
res = self.client.get("/admin/auditlog/logentry/")
assert res.status_code == 200
self.assertEqual(res.status_code, 200)
res = self.client.get("/admin/auditlog/logentry/add/")
assert res.status_code == 403
self.assertEqual(res.status_code, 403)
res = self.client.get(f"/admin/auditlog/logentry/{log_pk}/", follow=True)
assert res.status_code == 200
self.assertEqual(res.status_code, 200)
res = self.client.get(f"/admin/auditlog/logentry/{log_pk}/delete/")
assert res.status_code == 200
self.assertEqual(res.status_code, 200)
res = self.client.get(f"/admin/auditlog/logentry/{log_pk}/history/")
assert res.status_code == 200
self.assertEqual(res.status_code, 200)
def test_created_timezone(self):
log_entry = self.obj.history.latest()
for tz, timestamp in [
("UTC", "2022-08-01 12:00:00"),
("Asia/Tbilisi", "2022-08-01 16:00:00"),
("America/Buenos_Aires", "2022-08-01 09:00:00"),
("Asia/Kathmandu", "2022-08-01 17:45:00"),
]:
with self.settings(TIME_ZONE=tz):
self.assertEqual(self.admin.created(log_entry), timestamp)
class DiffMsgTest(TestCase):
@ -1274,9 +1297,22 @@ class DiffMsgTest(TestCase):
)
def test_changes_msg_delete(self):
log_entry = self._create_log_entry(LogEntry.Action.DELETE, {})
log_entry = self._create_log_entry(
LogEntry.Action.DELETE,
{"field one": ["value before deletion", None], "field two": [11, None]},
)
self.assertEqual(self.admin.msg(log_entry), "")
self.assertEqual(self.admin.msg_short(log_entry), "")
self.assertEqual(
self.admin.msg(log_entry),
(
"<table>"
"<tr><th>#</th><th>Field</th><th>From</th><th>To</th></tr>"
"<tr><td>1</td><td>field one</td><td>value before deletion</td><td>None</td></tr>"
"<tr><td>2</td><td>field two</td><td>11</td><td>None</td></tr>"
"</table>"
),
)
def test_changes_msg_create(self):
log_entry = self._create_log_entry(
@ -1287,6 +1323,9 @@ class DiffMsgTest(TestCase):
},
)
self.assertEqual(
self.admin.msg_short(log_entry), "2 changes: field two, field one"
)
self.assertEqual(
self.admin.msg(log_entry),
(
@ -1307,6 +1346,9 @@ class DiffMsgTest(TestCase):
},
)
self.assertEqual(
self.admin.msg_short(log_entry), "2 changes: field two, field one"
)
self.assertEqual(
self.admin.msg(log_entry),
(
@ -1331,6 +1373,7 @@ class DiffMsgTest(TestCase):
},
)
self.assertEqual(self.admin.msg_short(log_entry), "1 change: some_m2m_field")
self.assertEqual(
self.admin.msg(log_entry),
(

View file

@ -19,7 +19,7 @@ Automatically logging changes
Auditlog can automatically log changes to objects for you. This functionality is based on Django's signals, but linking
your models to Auditlog is even easier than using signals.
Registering your model for logging can be done with a single line of code, as the following example illustrates::
Registering your model for logging can be done with a single line of code, as the following example illustrates:
.. code-block:: python

View file

@ -28,9 +28,10 @@ setup(
description="Audit log app for Django",
long_description=long_description,
long_description_content_type="text/markdown",
python_requires=">=3.7",
install_requires=[
"django-admin-rangefilter>=0.8.0",
"python-dateutil>=2.6.0",
"python-dateutil>=2.7.0",
],
zip_safe=False,
classifiers=[
@ -42,6 +43,7 @@ setup(
"Framework :: Django",
"Framework :: Django :: 3.2",
"Framework :: Django :: 4.0",
"Framework :: Django :: 4.1",
"License :: OSI Approved :: MIT License",
],
)

View file

@ -1,7 +1,7 @@
[tox]
envlist =
{py37,py38,py39,py310}-django32
{py38,py39,py310}-django{40,main}
{py38,py39,py310}-django{40,41,main}
py37-docs
py38-lint
@ -14,6 +14,7 @@ commands =
deps =
django32: Django>=3.2,<3.3
django40: Django>=4.0,<4.1
django41: Django>=4.1,<4.2
djangomain: https://github.com/django/django/archive/main.tar.gz
# Test requirements
coverage