Add policy list to the policy store GraphQL type
What does this MR do and why?
Summary
This change adds the ability to query security policies stored in GitLab's policy store through the GraphQL API. Previously, policies could only be accessed via the REST API; now they can also be fetched using GraphQL queries.
A new GovernPolicy type is introduced, exposing policy details such as name, description, trigger type, rules, actions, enforcement mode, and lifecycle state. Users can retrieve all policies for an organization, with an optional filter to return only policies that respond to a specific trigger type.
Access is restricted to organization owners, and the feature is marked as experimental (introduced in GitLab 19.4). The change also includes proper permission checks and error handling — for example, returning null for groups (which don't hold policies) or when the user lacks the required access rights. Tests covering various access scenarios, filtering, and schema execution are included.
Closes https://gitlab.com/gitlab-org/gitlab/-/work_items/613019.
References
- Issue (confidential): https://gitlab.com/gitlab-org/gitlab/-/work_items/613019
- Epic: https://gitlab.com/groups/gitlab-org/-/epics/22027
- MR 1 of the stack: !250239 (merged)
- REST endpoint this mirrors: !249148 (merged)
- Published spec (members only): https://security-policies-knowledge-d7e8b2.gitlab.io/epics/22027-cd-deployment-policies/specs/613019-graphql-policy-store-api/
- Delivery evidence (members only): https://security-policies-knowledge-d7e8b2.gitlab.io/epics/22027-cd-deployment-policies/evidence/613019-delivery/
Verification
- Local: type + resolver specs green (
10 examples, 0 failures), including a schema-execution example resolvingorganization { policyStore { policies { id } } }through the real schema, a group-parent null case, and separate axes for the feature flag, instance setting, license, and organization filter. - Local:
bundle exec rake gitlab:permissions:validate→GraphQL permissions are valid(covers the granular-token doc row forGovernPolicyand the required shared-example coverage ofread_govern_policy); RuboCop clean; pre-push hooksgraphql_introspection_check,permissions-verify, andgraphql_docspassed. - The request spec (
ee/spec/requests/api/graphql/organizations/policy_store_policies_spec.rb) is judged by CI, including the granular-token shared example seeded with a real policy. - Adversarial review verdict on the pre-reshape diff, verbatim: pass with findings — accepted findings are deliberate experiment-stage decisions:
Intids matching the REST entity (the gem value object is not ActiveRecord), a plain list rather than a connection, and aStringtriggerType(unknown values return an empty list where REST returns 400; specced explicitly as a divergence). The reshape ontoPolicyStorealso resolves the review's naming-split finding (governPoliciesvspolicyStore*).
Screenshots or screen recordings
No UI changes.
How to set up and validate locally
-
In
rails console, activate the experiment (Ultimate license required):Feature.enable(:security_policies_v2) Gitlab::CurrentSettings.update!(policy_store_experiment_enabled: true) -
Seed a policy for your organization (id 1 in GDK):
Gitlab::PolicyStore.create(organization_id: 1, name: 'Test policy', trigger_type: 'deployment_requested') -
As an organization owner or admin, run in GraphiQL (
/-/graphql-explorer):{ organization(id: "gid://gitlab/Organizations::Organization/1") { policyStore { policies(triggerType: "deployment_requested") { id name triggerType mode lifecycleState } } } } -
Expect the seeded policy. As a non-owner organization user, expect
policiesto benullwhile the catalog fields onpolicyStorestill resolve.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.