Hide Secrets manager sidebar item from users who cannot open the page
What does this MR do and why?
The sidebar showed the Secrets manager item to every Reporter+ user, but the page enforces stricter checks. Under the paid experience, the onboarding actions (start trial, enable add-on, provision) are Owner-only. Once provisioned, the controller enforced the effective OpenBao read_metadata capability and returned 404 to users without a secrets permission grant. The sidebar and the controller disagreed about who can open the page.
This MR aligns the sidebar visibility check with the controller. Both menus (the group menu Sidebars::Groups::Menus::SecurityComplianceMenu and the EE extension of the project menu) already checked read_secret / read_project_secrets. After that existing check:
- When the manager is active, show the item only when the user has the effective OpenBao
read_metadatacapability, resolved throughSecretsManagement::UserPermissions::EffectiveCapabilitiesService. This is the same check the controllers enforce and applies with or without the paid experience flag. - When not provisioned, provisioning, or deprovisioning with the paid experience on, show the item only to users who can act on the setup page. Group menu:
admin_groupon the group, which is what the start-trial mutation and the page's own CTA check. Project menu:admin_groupon the top-level group (can start or pay for the trial) oradmin_project_secrets_manageron the project (can provision). Write abilities alone were wrong here becauseprovision_secrets_manageris denied intrial_eligible, the very state where the Owner has to start the trial. - Read-only entitlement states (
blocked) still permit metadata reads, so users with a grant keep the item.
The cross-request cache for the capabilities lookup is not introduced by this MR. It landed on master in !251010 (merged) (Gitlab::Cache.fetch_once, 30-second TTL, failures never written). This MR was rebased onto it and uses it as is. Around that cache this MR adds three things:
- An
http_timeout:option on the capabilities service, threaded through to a newtimeout:option onSecretsManagerClient, applied as open and read timeout on both Faraday connections, which previously had no timeout at all. The sidebar passes 3 seconds so a slow OpenBao hides the item rather than stalling every page render; the controller and GraphQL keep the defaults. - Fail-closed OpenBao errors are reported through
SecretsManagement::ThrottledErrorTracking(one Sentry event per error class per 5 minutes, the rest to logs), since the sidebar re-runs the rescue on every page render while OpenBao is down. - Cache invalidation on grant changes for user principals: the group and project secrets-permission update and delete services drop the cached capabilities entry (Redis and request store) for the granted or revoked user via
EffectiveCapabilitiesService.expire_for_user. Without it a freshly granted Reporter got 404 for up to 30 seconds after the Owner clicked Add (measured on GDK). Role and custom-role grants fan out to members that cannot be enumerated cheaply and still rely on the 30-second expiry, the trade-off !251010 (merged) described and left open for the user case.
Unit specs cover both menus across trial_eligible, trial, paid, offline_paid, blocked and ineligible in the not-provisioned, provisioning and active shapes for Reporter, Owner and project-only Owner. The client spec covers the timeout on both connections. The service specs cover the timeout pass-through, throttled reporting and expire_for_user; the permission service specs cover that a user grant expires the cache and a role grant does not. End to end: 11 scenarios per mode (SaaS and self-managed) in the Secrets Manager e2e harness against GDK with the fake CustomersDot and real OpenBao, all passing on the rebased head: trial-eligible, blocked before and after provisioning, active with and without a grant, grant, revocation, role grant, active project manager, subgroup, New badge, OpenBao outage and recovery. Three internal comments on this MR carry the reports: SaaS, self-managed, and the post-rebase run that adds revocation, role grants, the project manager, the subgroup, the badge and the OpenBao outage.
Closes https://gitlab.com/gitlab-org/gitlab/-/work_items/628416
Behaviour notes for reviewers
- An Owner without an OpenBao grant on an active manager no longer sees the item; the controller already 404s for them. Owners hold the default role grant created at provisioning, so in practice this affects only Owners whose default grant was removed.
- Project-only Owner in a blocked (read-only) entitlement state before the project manager exists: no item, because provisioning is denied and they cannot start or pay for the trial, while the page still renders by URL. This is the one remaining page-without-item case; the top-level group Owner keeps the item there.
- Role and custom-role grants and revocations reach the sidebar, page and API within the 30-second cache TTL, not instantly; user grants and revocations are instant.
- Danger asks for an authorization review because the project menu disables
Gitlab/Authz/PermissionCheckforadmin_project_secrets_manager(the ability the provisioning mutation authorizes; projects have no granular provision permission). The group menu'sadmin_groupcheck is exempted by the cop's todo list.
References
- https://gitlab.com/gitlab-org/gitlab/-/work_items/628416 (the issue)
- !251010 (merged) (master's capabilities cache this MR builds on)
- #601817 (closed) (in-page counterpart: empty state and hidden actions for users without permissions)
- https://gitlab.com/gitlab-org/gitlab/-/work_items/616139 (consolidation of availability checks)
- https://gitlab.com/gitlab-org/gitlab/-/work_items/600598 (introduced the previous sidebar condition)
- https://gitlab.com/gitlab-org/govern/secrets-management/secrets-manager-fulfillment-e2e (the e2e harness)
Screenshots or screen recordings
No visual change to the item itself. The MR changes only whether the Secrets manager item is present in the Secure menu, depending on the user's permissions and the entitlement state. Screenshots and videos for every scenario are in the three internal e2e report comments on this MR (SaaS, self-managed, and the post-rebase run).
How to set up and validate locally
- GDK with
GITLAB_SIMULATE_SAAS=1,Feature.enable(:secrets_manager_paid_experience), OpenBao running. - Create a top-level group. Add user A as Owner and user B as Reporter.
- As B: Secure > Secrets manager is not in the sidebar. The page by URL still shows the setup state without usable actions (unchanged, #601817 (closed)).
- As A: the item is shown. Start the trial; the group is provisioned.
- As B, reload: no item; the direct URL is 404.
- As A, grant B a secrets permission under Settings > General > Secrets manager.
- As B: the direct URL opens and the item is in the sidebar on the next page load.
- As A, delete the grant: B loses the item and the URL is 404 again on the next load.
- Repeat with a project in the group. Alternatively run
bin/run saas sidebarin the e2e harness.
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.