Skip to content

Commit e117e07

Browse files
committed
Security logs: delete old user security logs
1 parent 02a42b9 commit e117e07

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

readthedocs/audit/tasks.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Celery tasks related to audit logs."""
2+
3+
import logging
4+
5+
from django.conf import settings
6+
from django.utils import timezone
7+
8+
from readthedocs.audit.models import AuditLog
9+
from readthedocs.worker import app
10+
11+
log = logging.getLogger(__name__)
12+
13+
14+
@app.task(queue='web')
15+
def delete_old_personal_audit_logs(days=None):
16+
"""
17+
Delete personal security logs older than `days`.
18+
19+
If `days` isn't given, default to ``RTD_AUDITLOGS_DEFAULT_RETENTION_DAYS``.
20+
21+
We delete logs that aren't related to an organization,
22+
there are tasks com .com to delete those according to their plan.
23+
"""
24+
days = days or settings.RTD_AUDITLOGS_DEFAULT_RETENTION_DAYS
25+
days_ago = timezone.now() - timezone.timedelta(days=days)
26+
audit_logs = AuditLog.objects.filter(
27+
log_organization_id__isnull=True,
28+
created__lt=days_ago,
29+
)
30+
log.info("Deleting old audit logs. days=%s count=%s", days, audit_logs.count())
31+
audit_logs.delete()

readthedocs/audit/tests/test_tasks.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from unittest import mock
2+
3+
from django.contrib.auth.models import User
4+
from django.test import TestCase
5+
from django.utils import timezone
6+
from django_dynamic_fixture import get
7+
8+
from readthedocs.audit.models import AuditLog
9+
from readthedocs.audit.tasks import delete_old_personal_audit_logs
10+
from readthedocs.organizations.models import Organization
11+
from readthedocs.projects.models import Project
12+
13+
14+
class AuditTasksTest(TestCase):
15+
16+
def setUp(self):
17+
self.user = get(User)
18+
self.project = get(
19+
Project,
20+
slug='project',
21+
)
22+
self.organization = get(
23+
Organization,
24+
owners=[self.user],
25+
name='testorg',
26+
)
27+
self.organization.projects.add(self.project)
28+
29+
self.another_user = get(User)
30+
self.another_project = get(
31+
Project,
32+
slug='another-project',
33+
users=[self.user],
34+
)
35+
36+
@mock.patch('django.utils.timezone.now')
37+
def test_delete_old_personal_audit_logs(self, now_mock):
38+
now_mock.return_value = timezone.datetime(
39+
year=2021,
40+
month=5,
41+
day=5,
42+
)
43+
newer_date = timezone.datetime(
44+
year=2021,
45+
month=4,
46+
day=30,
47+
)
48+
middle_date = timezone.datetime(
49+
year=2021,
50+
month=4,
51+
day=5,
52+
)
53+
old_date = timezone.datetime(
54+
year=2021,
55+
month=3,
56+
day=20,
57+
)
58+
for date in [newer_date, middle_date, old_date]:
59+
for user in [self.user, self.another_user]:
60+
# Log attached to a project and organization.
61+
get(
62+
AuditLog,
63+
user=user,
64+
project=self.project,
65+
created=date,
66+
action=AuditLog.PAGEVIEW,
67+
)
68+
# Log attached to a project only.
69+
get(
70+
AuditLog,
71+
user=user,
72+
project=self.another_project,
73+
created=date,
74+
action=AuditLog.PAGEVIEW,
75+
)
76+
77+
# Log attached to the user only.
78+
get(
79+
AuditLog,
80+
user=user,
81+
created=date,
82+
action=AuditLog.AUTHN,
83+
)
84+
85+
# Log without a user.
86+
get(
87+
AuditLog,
88+
created=date,
89+
action=AuditLog.AUTHN_FAILURE,
90+
)
91+
92+
# Log with a organization, and without a user.
93+
get(
94+
AuditLog,
95+
project=self.project,
96+
created=date,
97+
action=AuditLog.AUTHN_FAILURE,
98+
)
99+
100+
self.assertEqual(AuditLog.objects.all().count(), 24)
101+
102+
# We don't have logs older than 90 days.
103+
delete_old_personal_audit_logs(days=90)
104+
self.assertEqual(AuditLog.objects.all().count(), 24)
105+
106+
# Only 5 logs can be deteled.
107+
delete_old_personal_audit_logs(days=30)
108+
self.assertEqual(AuditLog.objects.all().count(), 19)
109+
110+
# Only 5 logs can be deteled.
111+
delete_old_personal_audit_logs(days=10)
112+
self.assertEqual(AuditLog.objects.all().count(), 14)
113+
114+
# Only 5 logs can be deteled.
115+
delete_old_personal_audit_logs(days=1)
116+
self.assertEqual(AuditLog.objects.all().count(), 9)

readthedocs/settings/base.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ def TEMPLATES(self):
422422
'schedule': crontab(minute=0, hour=1),
423423
'options': {'queue': 'web'},
424424
},
425+
'every-week-delete-old-personal-audit-logs': {
426+
'task': 'readthedocs.audit.tasks.delete_old_personal_audit_logs',
427+
'schedule': crontab(day_of_week='wednesday', hour=7, minute=0),
428+
'options': {'queue': 'web'},
429+
},
425430
'every-day-resync-sso-organization-users': {
426431
'task': 'readthedocs.oauth.tasks.sync_remote_repositories_organizations',
427432
'schedule': crontab(minute=0, hour=4),

0 commit comments

Comments
 (0)