Implement create group secret mutation

What does this MR do and why?

This MR implements the create mutation for group-level secrets as part of the group secrets management feature. This is MR 2 in the implementation sequence, building on the foundation laid in !217485 (closed).

What this adds:

  • groupSecretCreate GraphQL mutation - Allows creating new group secrets with environment scoping and protected branch restrictions
  • GroupSecret model - Represents group secrets with validation and dirty tracking
  • GroupSecretType GraphQL type - Exposes group secret data through the API
  • Create and Read services for group secrets
  • Group-specific CI policy refresher for managing OpenBao access policies

Key implementation details:

Model architecture:

  • Introduces BaseSecret class that both ProjectSecret and GroupSecret inherit from
  • Refactors shared secret status logic into reusable SecretStatus concern
  • Renames ProjectSecretStatusEnum to SecretStatusEnum for use across both project and group secrets
  • GroupSecret tracks changes to environment and protected attributes for policy updates

Service layer:

  • CreateService - Handles secret creation with OpenBao integration
  • ReadService - Retrieves secret metadata from OpenBao
  • Shared service helpers extracted to Secrets::CreateServiceHelpers for reuse between project and group secrets
  • Implements two-phase creation: write secret value, then update metadata with completion timestamp

CI Policy management:

  • Introduces BaseSecretRefresher with shared policy refresh logic
  • GroupSecretRefresher - Manages group-specific policies based on environment and protected status
  • ProjectSecretRefresher - Refactored from SecretRefresher to inherit from base class
  • Policies are created/updated based on environment + protected combination for groups
  • Automatically removes policies when no secrets reference them

Authorization:

  • GroupSecretPolicy delegates authorization to the parent group
  • Uses write_secret permission (available to Reporter+ role)

Why this approach:

  • Group secrets use environment + protected for scoping (vs. project secrets which use environment + branch)
  • Protected flag determines if secret is only accessible from protected branches
  • Follows the same two-phase creation pattern as project secrets for consistency
  • Policy management ensures secrets are only accessible to authorized CI jobs

Dependencies

This MR depends on !218266 (merged) being merged first. Will rebase on master once merged.

Sample GraphQL Query

Create a group secret:

mutation {
  groupSecretCreate(input: {
    groupPath: "gitlab-org"
    name: "DATABASE_PASSWORD"
    description: "Production database password"
    secret: "super-secret-value"
    environment: "production"
    protected: true
  }) {
    groupSecret {
      name
      description
      environment
      protected
      metadataVersion
      status
      group {
        fullPath
      }
    }
    errors
  }
}

Related to #577342

This is MR 2 based on !217485 (comment 2989161362)

Edited by Erick Bajao

Merge request reports

Loading