Skip to content

Audit log shows a blank user for API key, system, and deleted-user changes #8261

Description

@Holmus

Is your feature request related to a problem?

Audit log entries frequently show an empty User column, with no indication of who or what made the change. A customer raised this on SaaS: "Sometimes no user is listed in the user section in the audit log. Do you know why this happens?"

There are three ways an entry ends up with author = null:

  1. The change was made with a Master API Key (Terraform provider, CI scripts, direct Admin API calls). AuditLog has a separate master_api_key FK alongside author, and it is populated correctly - we just never return it. AuditLogListSerializer and AuditLogRetrieveSerializer only expose author, so the API response has no attribution at all for these entries.
  2. System events (is_system_event=True), e.g. a scheduled Change Request going live via create_feature_state_went_live_audit_log. These legitimately have no human author, but the UI gives no signal that this is why the field is blank.
  3. The user was deleted. author is on_delete=models.SET_NULL, so removing a member from the organisation retroactively blanks their name on every historical entry they created.

In the UI this is worse than in the API, because AuditLog.tsx renders {author?.first_name} {author?.last_name} with no fallback - so all three cases render as an empty cell that looks like missing data rather than a known state.

The impact is that the audit log stops being a reliable answer to "who changed this flag", which is its primary job. For customers using it for compliance or for tracing an unexpected production change, a blank cell is indistinguishable from a bug. Note that is_system_event is already returned by the API and unused by the row rendering, and master_api_key attribution already exists in the database - so in both cases the information is present and simply not surfaced.

Describe the solution you'd like.

Never render an empty User cell. Specifically:

  1. Add the Master API Key to the audit log serializers (at minimum {id, name} from MasterAPIKey) so API consumers can attribute API-driven changes.
  2. In AuditLog.tsx, fall back through author -> master API key name (badged as an API key, e.g. 🔑 terraform-prod) -> System when is_system_event is true.
  3. For deleted users, show something explicit such as Deleted user rather than nothing. This one needs a data decision - see alternatives.

Item 1 and 2 are small and independent of item 3, and together cover the majority of reported blanks.

Describe alternatives you've considered

  • Denormalise the author's email onto AuditLog at write time. This solves case 3 properly (the historical record keeps the identity of whoever made the change, even after the user is deleted) but is a larger change and has GDPR implications for erasure requests, so it deserves its own discussion. Rendering Deleted user is the cheaper option and at least distinguishes "we don't know" from "no human did this".
  • Leave the API as-is and fix only the frontend. Rejected: customers query the audit log API directly for SIEM and compliance pipelines, and the attribution gap exists there too.
  • Document the behaviour instead of changing it. Doesn't help - the reason a user asks about this is that they need to know who made a specific change, and documentation doesn't give them the answer.

Additional context

Relevant code:

  • api/audit/models.py - AuditLog.author (SET_NULL), AuditLog.master_api_key, AuditLog.is_system_event
  • api/audit/serializers.py - AuditLogListSerializer / AuditLogRetrieveSerializer field lists, neither includes master_api_key
  • api/audit/tasks.py - _create_feature_state_audit_log_for_change_request creates is_system_event=True entries with no author
  • frontend/web/components/AuditLog.tsx - renders {author?.first_name} {author?.last_name} with no fallback

Worth noting that the flag change webhook already handles this correctly: trigger_feature_state_change_webhooks in api/features/tasks.py falls back to master_api_key.name for changed_by. So a customer receiving webhooks currently gets better attribution than the same customer reading the audit log UI. Aligning the audit log with that existing behaviour seems like the obvious baseline.

Possibly relevant to #6304 (Epic: Audit Log Enhancements).

Raised from a SaaS customer support conversation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions