Attribute system hook audit events to organizations
What does this MR do and why?
System hook audit events were attributed to the acting user: EE::WebHooks::CreateService and EE::WebHooks::DestroyService fell back to scope: current_user whenever a hook had no parent, so a SystemHook created or deleted on one organization landed in that user's audit log and could not be filtered per organization.
Any updates to webhooks were not audited at all because there was no WebHooks::UpdateService; the controller and API wrote to the model directly.
This MR:
- Scopes
SystemHookcreate and destroy audit events toGitlab::Audit::InstanceScope(matchingAdminDestroyService) and recordsorganization_idinadditional_details, via a new sharedEE::WebHooks::AuditHelpersmodule used by all three EE services.ProjectHook,GroupHookandServiceHookbehaviour is unchanged. - Adds a CE
WebHooks::UpdateServicewith an EE override that emits a newwebhook_updatedaudit event. The event lists only the names of changed attributes (encrypted columns mapped back totoken,url,url_variables,custom_headers;*_ivandupdated_atdropped) so secrets and URL credentials never reach the audit log. No event is emitted on a no-op save. - Routes every hook mutation path through the service:
WebHooks::HookActions#update,PUT /hooks/:id(API::Helpers::WebHooksHelpers#update_hook), and the URL variable and custom headerPUT/DELETEendpoints. Since those endpoints are mounted for project, group and system hooks, update auditing covers all three. - Extracts the shared validation error mapping into
WebHooks::ValidationErrorsso API response bodies are unchanged. - Adds
config/audit_events/types/webhook_updated.ymland regeneratesdoc/user/compliance/audit_event_types.md. - Adds an entry to
.labkit_logging_todo.ymlfor theorganization_iddetail now flattened intoGitlab::AuditJsonLoggerlines.
Why not a first-class Organization audit scope?
There is no Organization audit scope today: Gitlab::Audit::ScopeValidation, AuditEvents::BuildService, type_schema.json and the per-scope partitioned tables only know Project, Group, User and Instance. Introducing one is a cross-team change owned by group::compliance and is already raised as an open question in #607246. This MR therefore attributes the event to the organization through organization_id on an instance-scoped event, which is also what EE::Organizations::SoftDeleteService / HardDeleteService do. I suggest amending the issue wording accordingly.
References
- Closes #632601 (closed)
- Related: #607246 (Organization audit scope open question)
- Parent epic: gitlab-org#23936
Screenshots or screen recordings
No UI changes. Sample webhook_updated event details for a system hook:
{
"organization_id": 1,
"changed_attributes": ["url", "push_events", "custom_headers"]
}How to set up and validate locally
- Ensure an Ultimate licence is active (audit events are a licensed feature).
- Create a system hook as an admin and change it:
admin = User.admins.first hook = WebHooks::CreateService.new(admin).execute({ url: 'https://example.com/a' }, SystemHook.new)[:hook] WebHooks::UpdateService.new(admin).execute(hook, { url: 'https://example.com/b', token: 'secret' }) - Inspect the events:
Expect
AuditEvents::InstanceAuditEvent.where(target_type: 'SystemHook').order(:id).pluck(:event_name, :details)webhook_createdandwebhook_updated, both withorganization_id, andchanged_attributescontainingurlandtokenbut no values. - Confirm no
AuditEvents::UserAuditEventwas created for the admin. - Repeat via the API to cover the other paths:
curl -X PUT --header "PRIVATE-TOKEN: $TOKEN" \ "http://gdk.test:3000/api/v4/hooks/$HOOK_ID/url_variables/foo" --data "value=bar" - Create and update a project hook through the UI and confirm a
webhook_updatedevent scoped to the project appears under Settings > Audit events.
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 #632601 (closed)