Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/donations/models/ngos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from functools import partial
from typing import Any

from auditlog.registry import auditlog
from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import ValidationError
Expand Down Expand Up @@ -592,3 +593,7 @@ def can_receive_redirections(self):
def delete_prefilled_form(self):
if self.prefilled_form:
self.prefilled_form.delete()


auditlog.register(Ngo)
auditlog.register(Cause)
5 changes: 5 additions & 0 deletions backend/frequent_questions/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from auditlog.registry import auditlog
from django.db import models
from django.utils.translation import gettext_lazy as _
from tinymce.models import HTMLField
Expand Down Expand Up @@ -80,3 +81,7 @@ def get_all():
)

return questions


auditlog.register(Section)
auditlog.register(Question)
6 changes: 6 additions & 0 deletions backend/partners/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from auditlog.registry import auditlog
from django.db import models
from django.db.models import QuerySet
from django.db.models.functions import Lower
Expand Down Expand Up @@ -202,3 +203,8 @@ def save(self, *args, **kwargs):
self.display_order = number_of_partner_causes + 1

super().save(*args, **kwargs)


auditlog.register(Partner)
auditlog.register(PartnerNgo)
auditlog.register(PartnerCause)
1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies = [
"cryptography~=46.0.0",
"django~=5.2.0",
"django-allauth~=65.13.0",
"django-auditlog~=3.3.0",
"django-environ~=0.12.0",
"django-import-export~=4.3.0",
"django-ipware~=7.0.0",
Expand Down
2 changes: 2 additions & 0 deletions backend/redirectioneaza/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"django.contrib.sessions",
"django.contrib.staticfiles",
# third party apps:
"auditlog",
"django_q",
"django_recaptcha",
"django_vite",
Expand Down Expand Up @@ -116,6 +117,7 @@
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"partners.middleware.PartnerDomainMiddleware",
"allauth.account.middleware.AccountMiddleware",
"auditlog.middleware.AuditlogMiddleware",
]

AUTHENTICATION_BACKENDS = [
Expand Down
2 changes: 2 additions & 0 deletions backend/redirectioneaza/settings/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@
ENABLE_FULL_VALIDATION_CNP=(bool, True),
# Feature flags
ENABLE_MULTIPLE_FORMS=(bool, False),
#
AUDITLOG_EXPIRY_DAYS=(int, 1 * 365), # 1 year
)

environ.Env.read_env(ENV_FILE_PATH)
5 changes: 5 additions & 0 deletions backend/redirectioneaza/settings/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@
},
},
}

# Auditlog configuration

AUDITLOG_EXPIRY_DAYS = env.int("AUDITLOG_EXPIRY_DAYS")
AUDITLOG_INCLUDE_ALL_MODELS = False
11 changes: 11 additions & 0 deletions backend/redirectioneaza/settings/unfold.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@
},
],
},
{
"title": _("Audit Logs"),
"items": [
{
"title": _("Audit Logs"),
"icon": "history",
"link": reverse_lazy("admin:auditlog_logentry_changelist"),
"permission": lambda request: request.user.is_superuser,
},
],
},
{
"title": _("Background Tasks"),
"items": [
Expand Down
13 changes: 13 additions & 0 deletions backend/users/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import hmac
import uuid

from auditlog.registry import auditlog
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser, Group, UserManager
from django.db import models
Expand Down Expand Up @@ -177,3 +178,15 @@ class Meta:

verbose_name = _("Group")
verbose_name_plural = _("Groups")


auditlog.register(
User,
exclude_fields=[
"password",
"last_login",
"old_password",
"validation_token",
"token_timestamp",
],
)
19 changes: 19 additions & 0 deletions backend/utils/management/commands/cleanup_auditlog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import logging
from datetime import timedelta

from auditlog.models import LogEntry
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone

logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = "Deletes expired auditlog entries"

def handle(self, *args, **options):
cutoff_date = timezone.now() - timedelta(days=settings.AUDITLOG_EXPIRY_DAYS)
total_deleted, _per_category = LogEntry.objects.filter(timestamp__lt=cutoff_date).delete()

logger.info("Deleted %d expired auditlog entries", total_deleted)
43 changes: 43 additions & 0 deletions backend/utils/management/commands/schedule_auditlog_cleanup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging
from datetime import timedelta

from django.core.management import BaseCommand
from django.db.models import QuerySet
from django.utils import timezone
from django_q.models import Schedule
from django_q.tasks import schedule

logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = "Schedule an expired auditlog cleanup task to run once a day"
schedule_name = "CLEANUP_AUDITLOG"

def handle(self, *args, **kwargs):
logger.info("Scheduling auditlog cleanup")

self.remove_existing_schedules()
self.schedule_auditlog_cleanup()

logger.info("Auditlog cleanup scheduled successfully")

def remove_existing_schedules(self):
existing_schedules: QuerySet[Schedule] = Schedule.objects.filter(name=self.schedule_name)

if not existing_schedules.exists():
return

logger.info(f"Removing {existing_schedules.count()} existing schedules")

existing_schedules.delete()

def schedule_auditlog_cleanup(self):
schedule(
"django.core.management.call_command",
"cleanup_auditlog",
name=self.schedule_name,
schedule_type=Schedule.DAILY,
repeats=-1,
next_run=timezone.now() + timedelta(minutes=7),
)
15 changes: 15 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docker/s6-rc.d/init/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ python3 manage.py schedule_stats_generator_ngos_yearly "ngos_with_ngohub_per_yea
# Start the session clean-up schedule
echo "Starting the session clean-up schedule that runs once a day"
python3 manage.py schedule_session_cleanup

# Start the expired auditlog clean-up schedule
echo "Starting the expired auditlog clean-up scheduler"
python3 manage.py schedule_auditlog_cleanup