Skip to content

fix(cost-management): enable cluster/project RBAC filtering by registering dynamic permissions with RHDH (FLPATH-4207)#3193

Open
hardengl wants to merge 2 commits into
redhat-developer:mainfrom
hardengl:fix/rbac-cluster-permissions
Open

fix(cost-management): enable cluster/project RBAC filtering by registering dynamic permissions with RHDH (FLPATH-4207)#3193
hardengl wants to merge 2 commits into
redhat-developer:mainfrom
hardengl:fix/rbac-cluster-permissions

Conversation

@hardengl
Copy link
Copy Markdown
Contributor

@hardengl hardengl commented May 20, 2026

Summary

Cluster-specific RBAC permissions (ros/<cluster>, ros/<cluster>/<project>, and their cost/ equivalents) are created dynamically at runtime by fetchDynamicPermissions() but were never registered with createPermissionIntegrationRouter. RHDH's RBAC backend uses PluginPermissionMetadataCollector to validate permission names before evaluating them against Casbin policies — unregistered permissions default to DENY. This broke the entire 3-tier RBAC model: only ros.plugin (tier 1) ever worked.

Note — community Backstage vs RHDH: The upstream community RBAC backend may evaluate Casbin policies by name matching alone. However, RHDH's distribution (@red-hat-developer-hub/backstage-plugin-rbac-backend) requires permissions to be registered via createPermissionIntegrationRouter for them to be recognized by the authorization flow. This is confirmed by the A/B test evidence below.

Changes

router.ts — At router init, fetch cluster/project data from the upstream Cost Management APIs and register all ros/<cluster>, ros/<cluster>/<project>, cost/<cluster>, and cost/<cluster>/<project> permissions with the integration router. Uses Promise.allSettled so a partial failure (e.g. one API down) doesn't block other permissions from registering.

secureProxy.ts — Improved error logging and response messages. Previously returned opaque "Internal proxy error" for all non-SSO exceptions; now includes the request path and error details for easier debugging.

A/B Test Evidence — Permission Registration

Tested on live RHDH 1.9 cluster (ocp-edge73-0, OCP 4.19). Queried /api/permission/plugins/policies with identical configuration, only swapping the plugin image:

WITHOUT fix (stock 2.2.0): cost-management plugin registers 3 permissionsros.plugin, ros.apply, cost.plugin. Zero dynamic permissions. Any permissionsSvc.authorize() call for ros/ocp-edge73-0-xfvkt returns DENY.

WITH fix (2.2.0-rc.1): cost-management plugin registers 174 permissions — the same 3 static plus 171 dynamic cluster/project permissions across 57 unique clusters. Examples: ros/ocp-edge73-0-xfvkt, ros/ocp-edge73-0-xfvkt/rhdh-operator, ros/ocp-test-mc8h6, etc.

This confirms that createPermissionIntegrationRouter registration is required for RHDH's RBAC to evaluate these permissions. PRs #1996/#2102 correctly implemented the authorization logic (filterAuthorizedClustersAndProjects calling authorize() per cluster), but those calls always returned DENY because the permission names were not registered. This PR completes the circuit.

Proof the fix works

Verified on a live RHDH instance (ocp-edge73) with OCI image quay.io/gharden/cost-management-dynamic-plugins:2.2.0-rc.1.

Tier 1: ros.plugin — full access (396 containers, no filters)

costmgmt-full-access-optimizations

No permissions — access denied

costmgmt-no-access-optimizations

Tier 2: ros/<cluster> only — cluster-filtered (10 containers, down from 396)

ro-cluster-only-optimizations

Tier 3: ros/<cluster>/<project> only — cluster+project filtered

ro-project-only-optimizations

Backend audit logs

costmgmt-full-access: {"decision":"ALLOW","filters":{"clusters":[],"projects":[]}}            ← no filters
ro-cluster-only:      {"decision":"ALLOW","filters":{"clusters":["696794b9-..."],"projects":[]}}  ← cluster filter
ro-project-only:      {"decision":"ALLOW","filters":{"clusters":["696794b9-..."],"projects":["rhdh-operator"]}}
costmgmt-no-access:   {"decision":"DENY"}

CI

flightpath-ros #95 — 84 passed, 2 failed (pre-existing), 4 skipped. 20 of 21 FLPATH-4207 RBAC tests passed; 1 failure ("full-access user should see data across clusters") is under investigation.

Limitation

Dynamic permissions are registered once at startup. New clusters added after RHDH starts won't be recognized until the pod restarts. A periodic refresh mechanism would be a more complete solution.

Related

…roject tiers (FLPATH-4207)

Cluster-specific permissions (ros/<cluster>, ros/<cluster>/<project>) were
created at runtime but never registered with createPermissionIntegrationRouter.
The RHDH RBAC backend only evaluates registered permissions — unregistered ones
get DENY by default, breaking the entire 3-tier RBAC model.

At router init, fetch cluster/project data from upstream APIs and register all
dynamic permissions. Also improve secureProxy.ts error messages (include path
and error details instead of opaque "Internal proxy error").

Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-gh-app
Copy link
Copy Markdown

rhdh-gh-app Bot commented May 20, 2026

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/plugin-cost-management-backend workspaces/cost-management/plugins/cost-management-backend patch v2.2.0

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 60.46512% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.99%. Comparing base (815580b) to head (e0b1627).
⚠️ Report is 28 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3193      +/-   ##
==========================================
+ Coverage   60.97%   60.99%   +0.01%     
==========================================
  Files        2098     2098              
  Lines       65140    65181      +41     
  Branches    17029    17034       +5     
==========================================
+ Hits        39721    39756      +35     
- Misses      25180    25186       +6     
  Partials      239      239              
Flag Coverage Δ *Carryforward flag
adoption-insights 83.58% <ø> (ø) Carriedforward from e1cc0cc
ai-integrations 70.03% <ø> (ø) Carriedforward from e1cc0cc
app-defaults 69.60% <ø> (ø) Carriedforward from e1cc0cc
augment 69.36% <ø> (ø) Carriedforward from e1cc0cc
bulk-import 72.86% <ø> (ø) Carriedforward from e1cc0cc
cost-management 17.48% <60.46%> (+0.99%) ⬆️
dcm 32.85% <ø> (ø) Carriedforward from e1cc0cc
extensions 61.79% <ø> (ø) Carriedforward from e1cc0cc
global-floating-action-button 74.30% <ø> (ø) Carriedforward from e1cc0cc
global-header 61.68% <ø> (ø) Carriedforward from e1cc0cc
homepage 50.95% <ø> (ø) Carriedforward from e1cc0cc
konflux 91.01% <ø> (ø) Carriedforward from e1cc0cc
lightspeed 68.33% <ø> (ø) Carriedforward from e1cc0cc
mcp-integrations 81.59% <ø> (ø) Carriedforward from e1cc0cc
orchestrator 36.36% <ø> (ø) Carriedforward from e1cc0cc
quickstart 62.88% <ø> (ø) Carriedforward from e1cc0cc
sandbox 79.42% <ø> (ø) Carriedforward from e1cc0cc
scorecard 83.72% <ø> (ø) Carriedforward from e1cc0cc
theme 64.54% <ø> (ø) Carriedforward from e1cc0cc
translations 8.49% <ø> (ø) Carriedforward from e1cc0cc
x2a 78.28% <ø> (ø) Carriedforward from e1cc0cc

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 815580b...e0b1627. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Extract extractStrings() and buildClusterProjectPermissions() helpers
  to flatten nested if/for blocks (SonarCloud cognitive complexity 33→8)
- Add 8 unit tests covering both helpers (fulfilled/rejected/empty/dedup)
- Add changeset for cost-management-backend patch release

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@asmasarw asmasarw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - ran it locally and it works fine.

Copy link
Copy Markdown
Contributor

@PreetiW PreetiW left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RBAC changes here aren't working as expected — registering permissions in createPermissionIntegrationRouter only affects the metadata endpoint, not the authorization flow. Please refer to the Backstage Permission concepts and PRs #1996 / #2102 for the correct approach.

@hardengl hardengl changed the title fix(cost-management): register dynamic RBAC permissions for cluster/project tiers (FLPATH-4207) fix(cost-management): enable cluster/project RBAC filtering by registering dynamic permissions with RHDH (FLPATH-4207) May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants