Skip to content

Audit: log project deletions and capture IP/browser for async deletes#12870

Draft
ericholscher wants to merge 4 commits intomainfrom
eric/audit-project-delete
Draft

Audit: log project deletions and capture IP/browser for async deletes#12870
ericholscher wants to merge 4 commits intomainfrom
eric/audit-project-delete

Conversation

@ericholscher
Copy link
Copy Markdown
Member

Summary

  • Add a PROJECT_DELETE audit log action so project deletions are tracked in both user and organization security logs
  • Pass IP address and browser user-agent from the HTTP request through to the delete_object Celery task, so both the AuditLog entry and the django-simple-history HistoricalProject record capture this information
  • Create an audit log entry for every project admin (not just the deleting user) so all maintainers can see the deletion in their personal security log. The data field records who performed the deletion.

Closes #11001

Context

A support request revealed that when a project is deleted by one maintainer, other maintainers have no visibility into what happened. The existing AuditLog only tracks authentication, page views, downloads, and invitation events -- project deletions were not audited.

Additionally, because project deletion goes through an async Celery task (delete_object), the django-simple-history record was missing IP and browser information since there is no HTTP request available in the task context.

Changes

  • readthedocs/audit/models.py: Add PROJECT_DELETE action
  • readthedocs/audit/filters.py: Add PROJECT_DELETE to both user and organization security log filters
  • readthedocs/core/mixins.py: AsyncDeleteViewWithMessage now passes ip and browser to the Celery task
  • readthedocs/core/tasks.py: delete_object accepts ip/browser, sets them on HistoricalRecords.context, and creates AuditLog entries for project deletions
  • readthedocs/core/signals.py: add_extra_historical_fields falls back to reading ip/browser directly from HistoricalRecords.context when no HTTP request is available
  • Migration: 0009_add_project_delete_action

Test plan

  • Existing audit, history, and project deletion tests pass
  • Delete a project via the dashboard and verify an AuditLog entry with action project-delete is created for each project admin
  • Verify the HistoricalProject deletion record includes extra_history_ip and extra_history_browser
  • Verify the deletion appears in the user security log for all project admins
  • Verify the deletion appears in the organization security log

@ericholscher ericholscher requested a review from stsewd March 26, 2026 02:29
@ericholscher
Copy link
Copy Markdown
Member Author

I looked over this, and it seemed pretty self-explanatory.

@ericholscher ericholscher force-pushed the eric/audit-project-delete branch from c269f28 to 2013e30 Compare March 26, 2026 04:06
Comment on lines +102 to +106
from simple_history.models import HistoricalRecords

from readthedocs.audit.models import AuditLog
from readthedocs.organizations.models import Organization
from readthedocs.projects.models import Project
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these to be imported here? I don't think we'll hit a circular import with these.

# personal security log, not just the user who deleted it.
for project in projects_to_log:
for admin in project.users.all():
AuditLog.objects.create(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better done in a pre_delete signal on the Project model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit: extend log to cover project actions

2 participants