Add API Proxy | Add All Tabs to reflect API#2766
Add API Proxy | Add All Tabs to reflect API#2766asmasarw wants to merge 2 commits intoredhat-developer:mainfrom
Conversation
|
Important This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior. Missing ChangesetsThe following package(s) are changed by this PR but do not have a changeset:
See CONTRIBUTING.md for more information about how to add changesets. Changed Packages
|
Review Summary by QodoComprehensive DCM plugin refactor with API proxy, shared abstractions, and six new CRUD tabs
WalkthroughsDescription• **Comprehensive DCM plugin refactor** across three packages (dcm, dcm-backend, dcm-common) with improved code quality, reusability, and test coverage • **Frontend abstractions**: New useCrudTab hook centralizes CRUD state management; createYupValidator factory eliminates validation boilerplate; reusable components (DcmCrudTabLayout, DcmFormDialogActions, DcmDeleteDialog, DcmErrorSnackbar) reduce per-tab code duplication • **Shared library improvements**: New DcmBaseClient abstract class eliminates ~60 lines of duplicated fetch boilerplate; new DcmClientError custom error class carries HTTP status and structured RFC 7807 error data; extractApiError utility moved to dcm-common for shared use • **Backend hardening**: New secure API proxy route (/api/dcm/proxy/*) with automatic SSO token injection; improved tokenUtil error messages and cache alignment; dead code removal (GetTokenResponse.ts, setupTests.ts) • **Four new API clients**: CatalogClient, PolicyManagerClient, ProvidersClient, PlacementClient with full CRUD support and proper HTTP methods/headers • **Six new CRUD tabs**: Providers, Policies, Service Types, Catalog Items, Catalog Item Instances, Resources with live API integration replacing mock data • **Bug fix**: PolicyManagerClient.updatePolicy now sends correct `Content-Type: application/merge-patch+json` header • **Test coverage**: ~88 new tests across 10 test suites covering hooks, components, clients, utilities, and backend routes • **Dead code removal**: Deleted unused ExampleComponent directory and GetTokenResponse.ts Diagramflowchart LR
FE["Frontend Plugin<br/>dcm"]
BE["Backend Plugin<br/>dcm-backend"]
COMMON["Shared Library<br/>dcm-common"]
PROXY["API Proxy Route<br/>/api/dcm/proxy/*"]
SSO["SSO Token<br/>Acquisition"]
UPSTREAM["Upstream Services<br/>Catalog, Policy Manager,<br/>Providers, Placement"]
FE -- "useCrudTab hook<br/>DcmCrudTabLayout<br/>Form components" --> COMMON
FE -- "API refs<br/>CatalogApiRef<br/>PolicyManagerApiRef<br/>ProvidersApiRef<br/>PlacementApiRef" --> COMMON
COMMON -- "DcmBaseClient<br/>DcmClientError<br/>extractApiError" --> BE
BE -- "Proxy requests<br/>with bearer token" --> PROXY
PROXY -- "Inject SSO token" --> SSO
PROXY -- "Forward requests" --> UPSTREAM
UPSTREAM -- "JSON responses<br/>RFC 7807 errors" --> PROXY
PROXY -- "Response/error" --> BE
BE -- "Typed responses" --> FE
File Changes1. workspaces/dcm/plugins/dcm/src/hooks/useCrudTab.ts
|
Code Review by Qodo
|
| export function createDcmProxy(options: RouterOptions) { | ||
| return async (req: Request, res: Response): Promise<void> => { | ||
| const { logger, config } = options; | ||
|
|
||
| const apiGatewayUrl = config.getOptionalString('dcm.apiGatewayUrl'); | ||
| if (!apiGatewayUrl) { | ||
| logger.error( | ||
| 'dcm.apiGatewayUrl is not configured — cannot proxy DCM API requests.', | ||
| ); | ||
| res | ||
| .status(503) | ||
| .json({ error: 'DCM API gateway is not configured on the server.' }); | ||
| return; | ||
| } | ||
|
|
||
| // req.params[0] is the captured wildcard after /proxy/ | ||
| const wildcardPath = (req.params as Record<string, string>)[0] ?? ''; | ||
|
|
||
| const targetUrl = new URL( | ||
| `${API_BASE_PATH}/${wildcardPath}`, | ||
| apiGatewayUrl, | ||
| ); | ||
|
|
||
| // Forward all query parameters from the original request | ||
| const incomingParams = new URLSearchParams( | ||
| req.query as Record<string, string>, | ||
| ); | ||
| incomingParams.forEach((value, key) => { | ||
| targetUrl.searchParams.set(key, value); | ||
| }); | ||
|
|
||
| logger.debug( | ||
| `DCM proxy: ${req.method} ${req.path} → ${targetUrl.toString()}`, | ||
| ); | ||
|
|
||
| let tokenResult; | ||
| try { | ||
| tokenResult = await getTokenFromApi(options); | ||
| } catch (err) { | ||
| logger.error(`DCM proxy: failed to obtain access token — ${err}`); | ||
| res | ||
| .status(502) | ||
| .json({ error: 'Failed to obtain upstream access token.' }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
1. Proxy skips permission check 🐞 Bug ⛨ Security
createDcmProxy forwards requests for any authenticated caller but never runs the DCM permission authorization used elsewhere (authorize() via permissions.authorize), so users without DCM permissions can still reach upstream DCM APIs through /proxy. This is an authorization bypass relative to /token which explicitly denies unauthorized callers with 403.
Agent Prompt
## Issue description
The new DCM backend proxy (`/proxy/*`) forwards upstream DCM API requests for any authenticated user, but it does not enforce the DCM permission check used by `/token`.
## Issue Context
`/token` calls `authorize(req, options)` and returns `403` when the user lacks DCM permissions. The proxy should apply the same authorization before minting an upstream token and forwarding requests.
## Fix Focus Areas
- workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[31-75]
- workspaces/dcm/plugins/dcm-backend/src/routes/token.ts[23-35]
- workspaces/dcm/plugins/dcm-backend/src/routes/access.ts[22-34]
## Implementation notes
- Import and call `authorize(req, options)` early in the proxy handler.
- If denied, return `403 { error: 'Forbidden' }` (consistent with `/token`) and do not call `getTokenFromApi` or `fetch` upstream.
- Consider adding/adjusting tests in `proxy.test.ts` to verify a denied authorization returns 403.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
9649302 to
4bf2bfc
Compare
|



Overview
Introduces the DCM (Dynamic Configuration Management) plugin for Red Hat Developer Hub — a Backstage plugin that provides a self-service UI for managing infrastructure provisioning workflows through the DCM API Gateway.
Features
Providers management
Register, view, and manage service providers — the infrastructure backends that fulfil provisioning requests. Each provider entry includes its endpoint, service type, schema version, supported operations, and live health status.
Policy management
Create and manage placement policies that control how resources are allocated. Policies can be scoped globally or per-user and toggled enabled/disabled. A priority field controls evaluation order when multiple policies apply.
Catalog Items
Define reusable service blueprints (catalog items) backed by a service type. Each catalog item carries a configurable field schema that controls what parameters end users fill in when provisioning an instance.
Catalog Item Instances
Provision instances from catalog items by filling in the required field configuration. Instances represent active provisioning requests and can be deleted when no longer needed.
Resources
View and manage placement resources — the concrete infrastructure objects created by the Policy Engine. Resources can be created from a catalog item instance, deleted, or rehydrated (re-evaluated against current policies).
Backend capabilities
dcm.apiGatewayUrl. Tokens are cached server-side and refreshed automatically before expiry.dcm.pluginread permission gates both the UI and the/tokenendpoint, so users without the required permission cannot interact with the DCM API./accessendpoint the frontend uses to determine whether the current user has permission, enabling the UI to show appropriate messaging rather than silent failures.Configuration