Harden Secrets Manager add-on enable mutation

What does this MR do and why?

Follow-up to !253618 (merged), which introduced the SecretsManagerEnableAddOn GraphQL mutation. This MR fixes defects flagged in review at !253618 (comment 3790514834) and !253618 (comment 3791124895), which were deferred to a follow-up, plus one additional issue found by the author.

Fixes:

  1. Granular token scope. authorize_granular_token only required read_secrets_manager, so a read-only fine-grained token could convert a group to paid and provision it. The mutation now has a dedicated permission, enable_secrets_manager_add_on: a new raw permission (config/authz/permissions/secrets_manager_add_on/enable.yml, granted to Owner) and a new assignable permission group with a group boundary (config/authz/permission_groups/assignable_permissions/secrets_management/secrets_manager_add_on/enable.yml). Enabling a paid add-on is a billing action, so it gets its own permission rather than reusing the enrollment or provisioning ones, as suggested in review. Fine-grained token docs were regenerated to match.

  2. Conversion analytics. track_internal_event('secrets_manager_add_on_enabled') fired unconditionally after provisioning, so a failed provisioning attempt was billed but never counted, and repeat clicks over-counted. It now fires inside the same @add_on_stamped guard as the audit event, before provisioning runs. The event definition description was updated to reflect that it counts conversions once per group.

  3. Stale entitlement after opt-out. NamespaceEnrollmentService#unenroll cleared add_on_requested_at but not the entitlement resolver cache, and EE::Ci::RegisterJobService caches entitlement for up to a minute, so CI jobs could still read secrets briefly after opt-out. Both opt-out paths (unenroll, destroy_enrollment) now call SecretsManagement::Entitlement::Resolver.clear_cache.

  4. Non-atomic enroll + stamp. If stamping add_on_requested_at raised after enroll had already written the row, the rollback token never reached the caller. enroll_with_add_on_intent now compensates by reverting the enroll write before re-raising.

  5. Authorization ability. The mutation authorized admin_group, mirroring SecretsManagerStartTrial. It now authorizes the same enable_secrets_manager_add_on permission the granular directive uses, so the user-level ability and the token scope say the same thing. Same Owner-only floor, but GroupPolicy prevents it unless NamespaceEnrollment.enrollment_allowed? (SaaS, licence, and the secrets_manager_namespace_enrollment flag), so that gating applies at authorization instead of failing later inside the service, and the Gitlab/Authz/PermissionCheck disable is gone. provision_secrets_manager stays an explicit check right before provisioning, because the policy denies it while the group is still trial_eligible. One visible consequence: a subgroup path now gets the standard not-available error at authorization instead of the "top-level groups only" argument error, matching NamespaceSecretsManagerEnroll. Answers !253618 (comment 3791124895).

Also included: the GraphQL permissions docs generator (lib/tasks/gitlab/permissions/graphql/docs_task.rb) now dedupes permission groups, so a directive listing multiple raw permissions from the same group no longer emits a self-referential "Also requires" footnote.

Left for a separate MR, tracked in the issue: SecretsManagerStartTrial has the same read-permission and admin_group problems and will get a sibling start_secrets_manager_trial permission.

References

Screenshots or screen recordings

Backend-only change, no UI impact.

How to set up and validate locally

  1. Simulate SaaS and enable the flag: set GITLAB_SIMULATE_SAAS=1 in the environment, then in a Rails console run Feature.enable(:secrets_manager_paid_experience).
  2. Run the permission definition and docs checks: bundle exec rake gitlab:permissions:validate. All six checks should report valid / up-to-date.
  3. Run the specs: bundle exec rspec ee/spec/graphql/mutations/secrets_management/enable_add_on_spec.rb ee/spec/requests/api/graphql/mutations/secrets_management/enable_add_on_spec.rb ee/spec/services/secrets_management/namespace_enrollment_service_spec.rb spec/tasks/gitlab/permissions/graphql/docs_task_spec.rb
  4. To see fix 1 by hand: create a fine-grained personal access token with only the Secrets Manager: Read permission on a top-level group and call secretsManagerEnableAddOn with it; it must be denied. A token with Secrets Manager Add-on: Enable on the same group is accepted (the token owner still needs to be a group Owner).

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Dmytro Biryukov

Merge request reports

Loading
Loading