Add the policy store triggers endpoint

What does this MR do and why?

Adds GET /security/policy_store/triggers, the first of the Policy Store REST endpoints. It is deliberately small, so the naming and the shared gating can be settled before the rest follow.

Triggers come first because a policy targets exactly one trigger, and the trigger determines which rules and actions the policy can then use. Settling the trigger catalogue gives the later endpoints something to narrow against.

It carries the pieces every later endpoint builds on: the API::Govern::Policies Grape class, the three gates in the shared before block, and the mount. The catalogue itself lives in the gem, as Gitlab::PolicyStore::Triggers, so the domain owns it and the endpoint is only a way to read it.

Two naming decisions are worth arguing about here rather than eight MRs from now:

  • The URL is security/policy_store, not security/policies. v1 and v2 policies coexist, and security/policies is already v1's in the UI, at ee/config/routes/group.rb:244 and ee/config/routes/project.rb:96. v1 has no REST API yet, so taking that path would claim a name v1 would want later. policy_store matches the route the Policy Store already serves at /-/security/policy_store.
  • The Ruby namespace is API::Govern::Policies, with no V2 suffix. Every other V<n> namespace in the codebase versions a protocol whose version the caller sees in the URL, such as API::Conan::V1 serving ':id/packages/conan/v1'. Nothing here is versioned that way. Govern matches the bounded context that owns the store, the govern_policies table, and the Govern::Policy model.

The route takes no permission, and no authentication

The response is a static catalogue, identical for every caller, so there is nothing to authorize against. Reserving a read permission for the endpoints that return policy records keeps that permission meaningful.

Dropping the permission means dropping authenticate! with it. The granular access token guide allows skip_granular_token_authorization only on endpoints that do not authenticate by personal access token, and never as a way to bypass a permission check on one that does:

Use skip_granular_token_authorization exclusively for endpoints that are unauthenticated or authenticate by other means than a personal access token. Never use it to bypass permission checks on an endpoint that accepts PAT authentication.

doc/development/permissions/granular_access/rest_api_implementation_guide.md:261

So the route is :public_endpoint, alongside /templates/*, and appears in the generated token docs under the endpoints that fine-grained scope checks do not apply to.

One consequence is worth knowing: credentials are now ignored rather than validated, so an invalid token receives the catalogue instead of a 401. A spec records that.

The three gates in the before block are unchanged and still hide the endpoint entirely while the experiment is off.

The route is hidden from the OpenAPI paths while it is an experiment, per the REST API style guide, so the generated spec gains only the tag.

How to set up and validate locally

Requires an Ultimate licence.

  1. Enable the security_policies_v2 feature flag on the rails console
Feature.enable(:security_policies_v2)
  1. As an administrator, go to Admin > Settings > Security and compliance and turn on the policy store experiment
  2. List the triggers a policy can target, with no token, since the endpoint is public
curl --url "http://gdk.test:3000/api/v4/security/policy_store/triggers"
  1. Verify the response is
[
  { "id": "deployment_requested", "name": "Deployment" }
]
  1. Turn the instance setting back off, repeat the call, and verify it returns 404 rather than 403, so an instance that has not enabled the experiment does not confirm the endpoint exists

References

Edited by Marcos Rocha

Merge request reports

Loading
Loading