Secrets Manager: Cache EffectiveCapabilitiesService result across requests

Context

SecretsManagement::UserPermissions::EffectiveCapabilitiesService#execute calls OpenBao (login → sys/capabilities-self → revoke, 3 HTTP round-trips) to resolve a user's effective read/create/update/delete capabilities on a project or group secrets manager.

The service is called from two places that fire on a single page visit:

  1. The controllers' check_read_capability! when the user opens /secrets.
  2. The GraphQL userPermissions resolver when the frontend queries projectSecretsManager/groupSecretsManager.

These are two separate HTTP requests, so Gitlab::SafeRequestStore (which is already used inside the entitlement resolver for per-request caching) does not deduplicate across them. Each call currently triggers its own OpenBao round-trip triad, so a maintainer opening the page pays roughly 6 network round-trips to OpenBao just to render the page.

Proposal

Wrap EffectiveCapabilitiesService#execute in a short-TTL cross-request cache using Rails.cache.fetch, keyed by (secrets_manager_id, current_user.id).

The pattern is documented in doc/development/caching.md and used elsewhere in the codebase (e.g., licence caching in ee/app/models/license.rb, contribution analytics data collector, namespace storage alert).

Trade-offs

  • TTL choice. Something like 30 seconds is short enough that a maintainer who granted themselves an OpenBao permission sees the effect quickly, and long enough to cover the gap between page load and the follow-up GraphQL query. Worth benchmarking.
  • Invalidation on grant change. The SM permissions services (update_project_secrets_permission etc.) could bust the relevant cache entries after write, so the stale window is only relevant when the grant was changed out-of-band.
  • Failure mode. Fail-closed still applies: cache hits or misses, on OpenBao error we return all-false.
  • !240596 (merged) (the MR that surfaced the duplicate call).
  • #605081 (SM authorization consolidation), if it turns out the two callers can be simplified further.

Raised by @radbatnag's prod-readiness review of !240596 (merged).

Edited by 🤖 GitLab Bot 🤖