Add audit events for label changes

What does this MR do and why?

Adds audit events for label creation, update, and deletion on project and group labels. This addresses Audit label creation, update and destroy events (#415036 - closed).

AI use notice: this MR is heavily based on Duo authoring.

Changes

Three new audit event types

  • label_created — recorded when a project or group label is created
  • label_updated — recorded when a project or group label is updated. When the title changes, the message includes old and new values (e.g. Changed label title from Foo to Bar). For other field changes, a generic message is used (e.g. Updated label Foo), with additional_details capturing which attributes changed.
  • label_deleted — recorded when a project or group label is deleted

CE changes (minimal)

  • Labels::CreateService and Labels::UpdateService initializers now accept an optional current_user as the first argument, followed by params. All callers are updated.
  • New Labels::DestroyService wraps label.destroy. Previously, label deletion was done inline via @label.destroy in controllers and destroy_conditionally! in the API helper with no service object.
  • prepend_mod_with added to all three services for EE extension.

EE changes

  • EE service overrides (EE::Labels::CreateService, EE::Labels::UpdateService, EE::Labels::DestroyService) that call Gitlab::Audit::Auditor.audit after successful operations.
  • Three audit event YAML definitions in ee/config/audit_events/types/.

Design decisions

Explicit nil for non-audited callers

Callers that should not produce audit events (e.g., FindOrCreateService, Jira import, admin template labels, seed tasks) pass nil explicitly as current_user: .new(nil, params). The EE overrides skip auditing when current_user is nil. This makes it easy to grep for .new(nil, to find all non-audited call sites, and ensures new callers must make a conscious choice.

CE changes kept minimal

Audit logic lives entirely in EE. The only CE changes are: (1) adding current_user to the service initializers, (2) creating DestroyService, and (3) adding prepend_mod_with lines.

This follows the established pattern used by EE::Projects::ArchiveService, EE::Keys::DestroyService, and others.

Title changes get special treatment

Renaming a label directly impacts users: filters, automation, and integrations depend on label titles.

The audit message for title changes includes the old and new values. Description and color changes are "backwards compatible" and use a generic message, with changed attributes recorded in additional_details for programmatic consumers.

Auto-created labels are not audited.

Labels created automatically (e.g., via FindOrCreateService during issue triage, or Jira imports) do not generate audit events because no current_user is passed. This is intentional and documented. I chose to do it this way to keep this change small.

Out of scope

  • Admin/template labels — not audited (passed nil for current_user)
  • Auditing label promote operations

Two tests were added for template labels (destroy_service_spec.rb and update_service_spec.rb) solely to satisfy branch coverage requirements. When template label auditing is implemented, these coverage-only tests should be replaced with proper audit event assertions.

References

Screenshots or screen recordings

Before After
image
image

How to set up and validate locally

Prerequisites

  • GDK running with an EE/Ultimate license applied
  • A group and/or project with at least one label

Steps

  1. Verify audit events for label creation
    1. Go to a group or project, for example http://127.0.0.1:3000/groups/twitter/-/labels.
    2. Select New label and create a label with a title, description, and color.
    3. Go to the audit events page: http://127.0.0.1:3000/groups/twitter/-/audit_events.
    4. Verify an entry exists with action Created label .
    5. Repeat for a project label at http://127.0.0.1:3000/<project_path>/-/labels and verify the audit event at http://127.0.0.1:3000/<project_path>/-/audit_events.
  2. Verify audit events for label update
    1. Edit an existing label and change only the title.
    2. Verify the audit event says Changed label title from to .
    3. Edit the same label and change only the description.
    4. Verify the audit event says Updated label .
    5. Edit the same label and change only the color.
    6. Verify the audit event says Updated label .
  3. Verify audit events for label deletion
    1. Delete a label from the labels page.
    2. Verify the audit event says Deleted label .
  4. Verify API endpoints produce audit events
    # Create a label via API
    curl --header "PRIVATE-TOKEN: <your_token>" \
      --data "name=API+Label&color=%23FF0000" \
      "http://127.0.0.1:3000/api/v4/projects/<project_id>/labels"
    # Update a label via API
    curl --request PUT --header "PRIVATE-TOKEN: <your_token>" \
      --data "new_name=Renamed+API+Label" \
      "http://127.0.0.1:3000/api/v4/projects/<project_id>/labels/<label_id>"
    # Delete a label via API
    curl --request DELETE --header "PRIVATE-TOKEN: <your_token>" \
      "http://127.0.0.1:3000/api/v4/projects/<project_id>/labels/<label_id>"
  5. Verify auto-created labels are NOT audited
    # Trigger a Jira import or create an issue with a new label via the API
    curl --header "PRIVATE-TOKEN: <your_token>" \
      --data '{"title": "Test issue", "labels": "auto-created-label"}' \
      --header "Content-Type: application/json" \
      "http://127.0.0.1:3000/api/v4/projects/<project_id>/issues"
    Verify that no label_created audit event appears for auto-created-label.
  6. Verify admin/template labels are NOT audited
    1. Go to Admin > Labels (http://127.0.0.1:3000/admin/labels).
    2. Create, edit, and delete a template label.
    3. Verify no audit events are created (check Admin > Audit Events).
  7. Run the specs
    # CE destroy service spec
    bundle exec rspec spec/services/labels/destroy_service_spec.rb
    # Existing CE specs (verify no regressions)
    bundle exec rspec spec/services/labels/create_service_spec.rb
    bundle exec rspec spec/services/labels/update_service_spec.rb
    # EE audit event specs
    bundle exec rspec ee/spec/services/ee/labels/create_service_spec.rb
    bundle exec rspec ee/spec/services/ee/labels/update_service_spec.rb
    bundle exec rspec ee/spec/services/ee/labels/destroy_service_spec.rb
    # Audit event type definition validation
    bundle exec rake gitlab:audit_event_types:check_docs

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #415036 (closed)

Edited by Thiago Figueiró

Merge request reports

Loading