Secrets Manager: Consolidate authorization abilities

Context

Reviewing !240596 (merged) surfaced how confusing the permission zoo around Secrets Manager has become. There are at least three abilities whose relationship isn't obvious from the names alone:

  • :read_secrets_manager — maintainer+ (assignable permission, granular scope). Used by the granular directive and the enrollment/entitlement types.
  • :read_project_secrets_manager_status / :read_group_secrets_manager_status — reporter+ (via reporter.yml). Used by the SM type/resolver as the CE session gate.
  • :read_secrets_permission — maintainer+. Used to view the assigned permissions grid.
  • :read_secret / :read_project_secrets — reporter+ on group / reporter+ on project. Used to read individual secret values, but has been reused historically to gate other things too.
  • Plus the SM disabled/entitlement prevent rules and the OpenBao capability layer sit on top.

Each was added incrementally; the naming and the access boundaries diverged over time. Contributors have to reverse-engineer the intent before every change.

Goal

Audit the SM ability surface end to end and either:

  1. Merge/rename abilities so each concept has a single canonical name, or
  2. Document the intended split clearly (for example a README.md in the policies directory, or an ADR under .agent-docs/secrets-manager/).

Known concrete inconsistencies

  • Entitlement rule blocks group SM status but not project SM status. ee/app/policies/ee/group_policy.rb's secrets_manager_entitlement_denies_access rule prevents :read_group_secrets_manager_status, so an entitlement-denied group returns a top-level access error on groupSecretsManager queries. The equivalent project rule in ee/app/policies/ee/project_policy.rb does not prevent :read_project_secrets_manager_status, so an entitlement-denied project still returns status: 'ACTIVE' from projectSecretsManager — misleading, since the SM is actually inaccessible.

    The status enum returns pure lifecycle state (PROVISIONING / ACTIVE / DEPROVISIONING) with no entitlement awareness, so the current project behavior gives callers no way to distinguish "usable" from "entitlement-denied". The group side is the more correct behavior; project should match by adding prevent :read_project_secrets_manager_status to its entitlement-denies rule, along with a request spec mirroring the group's access_control_spec context added in !243141 (merged).

    Related history: the group side blocks status only by accident — the entitlement rule was designed to gate secret values and admin, and Ahmad's !234896 (merged) had reused :read_secret for the group SM type authorize. !240596 (merged) then introduced the dedicated :read_group_secrets_manager_status and explicitly added it to the entitlement prevent block to keep the behavior tested in !243141 (merged) (D2).

References

  • !240596 (merged) (thread that surfaced this: !240596 (comment 3530371487))
  • !234896 (merged) (introduced :read_secret on GroupSecretsManagerType because :read_secrets_manager was maintainer-only)
  • !242456 (merged) (introduced the entitlement-denies rules; asymmetric preventions)
  • !243141 (merged) (D2 write-entitlement enforcement; added the group entitlement-denies read test)
  • config/authz/permissions/secrets_manager/read.yml
  • config/authz/roles/{reporter,maintainer,auditor}.yml
  • ee/app/policies/ee/{project,group}_policy.rb

Raised by @hmehra during review of !240596 (merged).

Edited by Erick Bajao