Add a create mutation to the policy store GraphQL API
What does this MR do and why?
This merge request adds a new GraphQL API operation (GovernPolicyCreate) that allows users to create security policies in the policy store for an organization. The feature is introduced as an experimental capability (GitLab 19.4) and requires the user to be an organization owner with appropriate permissions.
The operation accepts policy details such as name, description, trigger type, rules, actions, enforcement mode, and scope, then saves the policy to the policy store. It includes proper error handling: access is silently blocked for unauthorized users or when the feature is disabled, validation errors are returned as user-facing messages, and unexpected internal failures are logged and surfaced as a generic error message. Tests cover successful creation, invalid inputs, permission checks, and feature flag/license gating.
References
- Related to https://gitlab.com/gitlab-org/gitlab/-/issues/617785 (also tracks CI evidence, hence not "Closes")
- Plan and decision record: https://gitlab.com/gitlab-org/gitlab/-/issues/617785#note_3705587478
- Stacked on !250240 (merged) (adds the
policiesfield andGovernPolicytype), which has merged — this MR now targetsmasterdirectly and is rebased on it.
Screenshots or screen recordings
No UI changes.
How to set up and validate locally
- In a rails console, enable the feature flag:
Feature.enable(:security_policies_v2) - Enable the experiment setting:
Gitlab::CurrentSettings.current_application_settings.update!(policy_store_experiment_enabled: true) - Ensure the instance/group has an Ultimate license.
- Sign in as an organization owner (or admin) and open GraphiQL at
/-/graphql-explorer. - Run:
mutation {
governPolicyCreate(input: {
organizationId: "gid://gitlab/Organizations::Organization/1",
name: "Block deploys to production",
triggerType: "deployment_requested",
rules: [{ type: "environment", value: { names: ["production"] } }],
actions: [{ type: "block" }]
}) {
policy {
id
name
triggerType
}
errors
}
}Expect:
{
"data": {
"governPolicyCreate": {
"policy": {
"id": 1,
"name": "Block deploys to production",
"triggerType": "deployment_requested"
},
"errors": []
}
}
}- Confirm it's listed, via the
policiesfield added in MR 250240:
query {
organization(id: "gid://gitlab/Organizations::Organization/1") {
policyStore {
policies {
id
name
}
}
}
}Specs: ee/spec/requests/api/graphql/mutations/govern/policy_create_spec.rb (happy path, empty rules rejection, store validation failure, conflicting policy_scope+scope_rego, unmapped-reason tracking, non-owner/outside-user denial, flag/setting/license off → resource-not-available, granular-token shared example) and ee/spec/graphql/mutations/govern/policy_create_spec.rb (schema assertions). Local result: 19 examples, 0 failures, 1 pending (granular-token shared example's built-in skip for instance boundaries). Rubocop clean.
rake gitlab:graphql:update_all, rake gitlab:permissions:graphql:compile_docs, rake gitlab:permissions:routes:compile_docs, and rake gitlab:permissions:validate were run and are green; generated docs and introspection schema changes are committed.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist.