Add duo_flow_callback_enabled to the hooks API and webhook form
What does this MR do and why?
Lets a Maintainer or Owner mark a project or group webhook as a Duo flow callback endpoint, from the webhook settings form or over the REST API. This is the configuration half of Duo flow webhook callbacks; !249147 (merged) is the half that attaches a marked webhook to a running flow.
Stacked on !249147 (merged), which defines the duo_flow_callback_hooks flag this MR reads.
Three things you cannot see from the diff:
- The container comes from the controller, not from
hook.parent. On the create form the hook is unsaved, sohook.parentisnil— andFeature.enabled?(flag, nil)falls through to the global gate, showing the section to every namespace. System hooks have no container, so they never show it. - The parameter is only permitted when the feature is available. So a hand-rolled
POST hook[duo_flow_callback_enabled]=truecannot set the attribute where the flag is off, nor on a system hook at all. The same predicate decides whether the checkbox renders, so the form and the guard cannot drift apart. - Enabling the attribute over the API where the feature is unavailable returns 400, rather than succeeding with the field quietly dropped. Disabling is always accepted, so a client can safely PUT back a payload it read from GET. Reads expose the field whatever the flag state, so the response shape does not change.
Not a security fix: the attribute is inert while the flag is off, because the trigger and delivery paths check it independently. The checkbox is an EE partial rendered through render_if_exists; the column and the REST parameter are CE, since web_hooks is a CE table.
References
- Issue: #606803 (closed)
- Epic: gitlab-org#22921
- Docs follow-up, including the anchor the Learn more link should point at: #618579
- Split from !248060 (closed)
Screenshots or screen recordings
Walkthrough
A 66-second capture of the whole path: the section renders with the flag on, the checkbox is ticked and saved, it survives a reopen, the REST API agrees, and the section disappears once the flag is turned off.
Project webhook, edit form
| Flag on | Flag off |
|---|---|
![]() |
![]() |
The setting survives a save
Ticked, saved, reopened — still ticked, and GET /projects/:id/hooks/:hook_id reports duo_flow_callback_enabled: true.
The section stays after a failed update
An invalid submission re-renders the form instead of redirecting, and the section is still there.
Group webhook
A hand-rolled request cannot set the field
With the flag off, a form POST that skips the UI does not persist the attribute.
$ curl .../api/v4/features | jq '.[] | select(.name=="duo_flow_callback_hooks")'
[{"key": "boolean", "value": false}, {"key": "expression", "value": null}, {"key": "actors", "value": ["Group:2281"]}]
# no Group:1000000 actor => the flag is OFF for the gitlab-duo namespace
$ sign in as $USER (session cookie, not a token)
# signed in
$ POST /gitlab-duo/test/-/hooks hook[duo_flow_callback_enabled]=true
# target url: https://example.com/duo-flag-off-probe-1787661613
# HTTP 302 -> http://gdk.test:3000/gitlab-duo/test/-/hooks
$ find the hook that was just created (url is encrypted, so look it up over the API)
# hook id: 26
$ read the column straight from the database
hook id=26 url=https://example.com/duo-flag-off-probe-1787661613
duo_flow_callback_enabled = false
PASS 7: the request set hook[duo_flow_callback_enabled]=true, the column is still false
cleanup: probe hook deletedHow to set up and validate locally
Enable the flag for a top-level group:
Feature.enable(:duo_flow_callback_hooks, Group.find_by_full_path('my-group'))-
The setting appears, and it sticks. On that group, or a project inside it, go to Settings > Webhooks. Tick Send Duo flow events to this webhook, save, then reopen the webhook — it should still be ticked.
-
The flag gates the form and the API together. Disable the flag. The section disappears from the form, and setting the field over the API is rejected:
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \ --url "https://gitlab.example.com/api/v4/projects/<project_id>/hooks/<hook_id>" \ --data "duo_flow_callback_enabled=true" # => 400 -
System hooks stay out of it. With the flag enabled, visit Admin > System hooks. The page renders normally and the section is absent.
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.




