Expose scan profile permissions in GraphQL

What does this MR do and why?

Expose scan profile permissions in GraphQL

Add readSecurityScanProfiles, createSecurityScanProfiles,
updateSecurityScanProfiles, deleteSecurityScanProfiles and
applySecurityScanProfiles to GroupPermissions

Resolve read, create, update, and delete permissions against the
root ancestor because scan profiles belong to the top-level namespace.
Resolve apply permission against the queried resource because attach
authorizes per project through Project.projects_user_can, and
maintainer.yml grants the ability at any level

Return false for GitLab-recommended profiles,
which the update and delete mutations already refuse

Mark the fields as experiment for milestone 19.4

Related to https://gitlab.com/gitlab-org/gitlab/-/issues/624191
Changelog: added
EE: true

Why?

Exposing these permissions will help us with [Frontend] Scan profile drawer: view, create, e... (#624191 - closed): We need to show each action only if the user has permission to take it, and this helps us do that without passing a boolean for every permission through Groups::SecurityFeaturesHelper. Plus we can fetch them elsewhere in the future without adding the booleans to yet another controller.

References

Screenshots or screen recordings

No visual changes expected - here are some graphiql screenshots anyway:

subgroup maintainer can only apply profiles in their subgroup top-level group owner has all permissions
Screenshot_2026-08-28_at_22.30.48 Screenshot_2026-08-28_at_22.32.25

How to set up and validate locally

  1. Make sure your GDK has an Ultimate licence, so the security_scan_profiles licensed feature is available. Without it the group policy prevents all five abilities and every field returns false.

  2. In GraphiQL at /-/graphql-explorer, query userPermissions on a group:

    query {
      group(fullPath: "gitlab-org") {
        userPermissions {
          readSecurityScanProfiles
          createSecurityScanProfiles
          updateSecurityScanProfiles
          deleteSecurityScanProfiles
          applySecurityScanProfiles
        }
      }
      group(fullPath: "gitlab-org/another-subgroup") {
        userPermissions {
          readSecurityScanProfiles
          createSecurityScanProfiles
          updateSecurityScanProfiles
          deleteSecurityScanProfiles
          applySecurityScanProfiles
        }
      }
    }
  3. The permissions returned by the query should match the role you have in the root group:

Role on the top-level group read create / update / delete apply
Non-member, Guest, Planner, Reporter false false false
Developer true false false
Maintainer, Owner, Security Manager true true true

... and if you don't hold any role in the root group, they should match the role you have in the subgroup - apply is the only field that can be true without root-group access:

Role on the subgroup only read create / update / delete apply
Minimal Access, Guest, Planner, Reporter, Developer false false false
Maintainer, Owner, Security Manager false false true
Edited by Miranda Fluharty

Merge request reports

Loading
Loading