Implement create/update services for group secret
What does this MR do and why?
This MR implements the create and update services for group-level secrets, preparing the foundation for the GraphQL mutations. This is MR 1 in the implementation sequence, focusing on the service layer and model refactoring.
What this adds:
-
GroupSecretmodel - Represents group secrets with validation and dirty tracking -
BaseSecretclass - Shared base class for bothProjectSecretandGroupSecret - Create and Update services for group secrets
- Group-specific CI policy refresher for managing OpenBao access policies
- Service helper modules for shared create/update logic
Key implementation details:
Model architecture:
- Introduces
BaseSecretclass that bothProjectSecretandGroupSecretinherit from - Extracts common attributes:
name,description,environment,metadata_version, timestamps - Extracts common validations: name format, presence checks
- Each subclass adds its own specific attributes:
-
ProjectSecret:branch(for branch-based scoping) -
GroupSecret:protected(for protected branch restriction)
-
- Dirty tracking for policy-relevant attributes (
environment,protectedfor groups;environment,branchfor projects)
Service layer refactoring:
-
Shared helpers extracted:
-
Secrets::CreateServiceHelpers- Common create logic for both project and group secrets -
Secrets::UpdateServiceHelpers- Common update logic for both project and group secrets
-
-
Group-specific services:
-
GroupSecrets::CreateService- Handles group secret creation with OpenBao integration -
GroupSecrets::ReadService- Retrieves group secret metadata from OpenBao -
GroupSecrets::UpdateService- Updates group secrets with optimistic locking
-
-
Refactored project services:
-
ProjectSecrets::CreateService- Now uses shared helpers -
ProjectSecrets::UpdateService- Now uses shared helpers - Significantly reduced code duplication
-
CI Policy management:
-
Base refresher:
CiPolicies::BaseSecretRefresherwith shared policy refresh logic -
Group refresher:
GroupSecretRefresher- Manages policies based onenvironment+protected -
Project refresher:
ProjectSecretRefresher- Manages policies based onenvironment+branch - Policies are created/updated when secrets are created/updated
- Policies are automatically removed when no secrets reference them
- Group secrets refresh policies before metadata update (to count secrets correctly)
- Project secrets refresh policies after metadata update (to update JWT role)
Two-phase creation/update:
-
Start phase: Write secret value + metadata with
started_attimestamp - Policy refresh: Update OpenBao policies for CI access
-
Complete phase: Update metadata with
completed_attimestamp - Status tracking: Secrets have lifecycle status (COMPLETED, CREATE_IN_PROGRESS, etc.)
Authorization:
-
GroupSecretPolicydelegates authorization to the parent group - Uses existing
write_secretandread_secretpermissions
Why this approach:
- Group secrets use
environment+protectedfor scoping (vs. project secrets which useenvironment+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
- Shared helpers reduce code duplication and ensure consistent behavior
Code organization
Before (project secrets only):
ProjectSecret (standalone model)
ProjectSecrets::CreateService (all logic inline)
ProjectSecrets::UpdateService (all logic inline)
CiPolicies::SecretRefresher (project-specific)
After (supports both project and group secrets):
BaseSecret (shared base class)
├── ProjectSecret (inherits from BaseSecret)
└── GroupSecret (inherits from BaseSecret)
Secrets::CreateServiceHelpers (shared create logic)
Secrets::UpdateServiceHelpers (shared update logic)
ProjectSecrets::CreateService (uses shared helpers)
ProjectSecrets::UpdateService (uses shared helpers)
GroupSecrets::CreateService (uses shared helpers)
GroupSecrets::UpdateService (uses shared helpers)
CiPolicies::BaseSecretRefresher (shared policy logic)
├── ProjectSecretRefresher (project-specific policies)
└── GroupSecretRefresher (group-specific policies)
Related
Related to #577342
This is MR 1 based on !218365 (comment 3004462962)
Next steps:
- MR 2: Add GraphQL mutations (
groupSecretCreate,groupSecretUpdate)
Edited by Erick Bajao