Add CARD API integration spec, Atlas metrics updates, NavDrawer and server.js cleanup, reference docs
This commit is contained in:
@@ -125,7 +125,7 @@ Add a tab system to the Metric Graphs panel on the ReportingPage, with an "Ivant
|
||||
- Verify `getStatusColor` returns `#10B981` for "active", `#EF4444` for "expired", `#0EA5E9` for "completed", `#64748B` for any other string
|
||||
- **Validates: Requirements 5.2**
|
||||
|
||||
- [~]* 5.8 Write unit tests for Atlas donut components
|
||||
- [ ]* 5.8 Write unit tests for Atlas donut components
|
||||
- Test Coverage donut empty state message when totalHosts is 0
|
||||
- Test Plan type donut empty state message when totalPlans is 0
|
||||
- Test Plan status donut empty state message when totalPlans is 0
|
||||
|
||||
1
.kiro/specs/card-api-integration/.config.kiro
Normal file
1
.kiro/specs/card-api-integration/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"specId": "0334e0b6-7ae7-4284-95a0-caed55c59af1", "workflowType": "requirements-first", "specType": "feature"}
|
||||
163
.kiro/specs/card-api-integration/requirements.md
Normal file
163
.kiro/specs/card-api-integration/requirements.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
This feature integrates the CARD API into the STEAM Security Dashboard so that CARD workflow items in the Ivanti Queue can trigger real actions — confirm, decline, redirect, and search — via the CARD API. The integration covers OAuth token management, a backend helper module with automatic update_token handling, specific proxy routes for each CARD operation, and frontend UI updates that let users execute CARD actions directly from the queue. A standalone asset search capability supports Granite ID lookups when assets are reassigned.
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Dashboard**: The STEAM Security Dashboard — the self-hosted vulnerability management application this feature extends.
|
||||
- **CARD_API**: The external CARD REST API hosted at `card.charter.com` (production) or `card.caas.stage.charterlab.com` (UAT), authenticated via OAuth Bearer tokens. Read endpoints use the `/api/v1/` path prefix; mutation endpoints use the `/api/v2/` path prefix.
|
||||
- **CARD_Helper**: The new `backend/helpers/cardApi.js` module responsible for CARD API authentication, token management, and HTTP request execution.
|
||||
- **Token_Manager**: The component within CARD_Helper that handles OAuth token acquisition via Basic Auth, in-memory caching, and automatic refresh before expiry. Tokens have a one-hour TTL.
|
||||
- **Queue_Item**: A row in the `ivanti_todo_queue` table with `workflow_type = 'CARD'`, representing a finding staged for CARD action.
|
||||
- **CARD_Route**: The new Express route module at `backend/routes/cardApi.js` that exposes CARD API operations to the frontend through the backend.
|
||||
- **Audit_Logger**: The existing `logAudit(db, {...})` helper that records state-changing actions to the `audit_logs` table.
|
||||
- **Auth_Middleware**: The existing `requireAuth(db)` and `requireGroup(...)` middleware that enforces session validation and role-based access.
|
||||
- **Asset_ID**: A CARD asset identifier in IPN format (e.g., `98.8.142.56-NATL`). Used as the path parameter in owner lookup and mutation endpoints.
|
||||
- **Update_Token**: A server-generated token returned by the GET owner endpoint. The update_token is mandatory for all mutation calls (confirm, decline, redirect) and ensures optimistic concurrency control.
|
||||
- **Disposition**: The ownership state of an asset in CARD. Valid values are `confirmed`, `unconfirmed`, `declined`, and `candidate`.
|
||||
- **Team**: A CARD team name (e.g., `NTS-AEO-STEAM`). Teams are the organizational unit for asset ownership in CARD.
|
||||
- **Owner_Record**: The JSON object returned by the GET owner endpoint, containing the asset ownership details, disposition states with team names, scores, timestamps, and the update_token field.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: CARD API Helper Module
|
||||
|
||||
**User Story:** As a backend developer, I want a dedicated CARD API helper module that follows the existing atlasApi.js pattern, so that all CARD API communication is centralized and consistent with the codebase.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE CARD_Helper SHALL export an `isConfigured` boolean that is `true` only when all required environment variables (`CARD_API_URL`, `CARD_API_USER`, `CARD_API_PASS`) are present and non-empty.
|
||||
2. WHEN `isConfigured` is `false`, THE CARD_Helper SHALL log a warning at module load listing the missing environment variables with the prefix `[card-api]`.
|
||||
3. THE CARD_Helper SHALL use the Node.js built-in `https` module for all HTTP requests to the CARD_API.
|
||||
4. THE CARD_Helper SHALL export convenience wrapper functions for GET and POST HTTP methods, each accepting a URL path, optional request body, and optional options object.
|
||||
5. THE CARD_Helper SHALL set `rejectUnauthorized` to `false` on HTTPS requests when the `CARD_SKIP_TLS` environment variable is set to `'true'`.
|
||||
6. THE CARD_Helper SHALL apply a configurable request timeout defaulting to 15000 milliseconds.
|
||||
7. THE CARD_Helper SHALL return a Promise that resolves with an object containing `status` (HTTP status code) and `body` (response body string) for each request.
|
||||
8. THE CARD_Helper SHALL route read requests (GET) through the `/api/v1/` path prefix and mutation requests (POST) through the `/api/v2/` path prefix, matching the CARD_API versioning scheme.
|
||||
|
||||
### Requirement 2: OAuth Token Management
|
||||
|
||||
**User Story:** As a backend developer, I want the CARD helper to manage OAuth Bearer tokens automatically, so that downstream code does not need to handle authentication directly.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a CARD API request is made and no cached token exists, THE Token_Manager SHALL acquire a new token by sending a request to the CARD_API `/api/v1/auth/get_token` endpoint with a Basic Auth header containing the base64-encoded `CARD_API_USER:CARD_API_PASS` credentials.
|
||||
2. WHEN a valid token is received, THE Token_Manager SHALL cache the token in memory along with its expiry timestamp (one-hour TTL from acquisition time).
|
||||
3. WHEN a cached token exists and its expiry timestamp is more than 60 seconds in the future, THE Token_Manager SHALL reuse the cached token for subsequent requests.
|
||||
4. WHEN a cached token exists and its expiry timestamp is 60 seconds or less in the future, THE Token_Manager SHALL acquire a new token before making the API request.
|
||||
5. THE Token_Manager SHALL include the cached Bearer token in the `Authorization` header of all non-authentication CARD API requests.
|
||||
6. IF the CARD_API returns an HTTP 401 response on a non-authentication request, THEN THE Token_Manager SHALL invalidate the cached token, acquire a new token, and retry the original request exactly once.
|
||||
7. IF the token acquisition request fails or returns a non-success HTTP status, THEN THE Token_Manager SHALL reject the Promise with a descriptive error message including the HTTP status code and the response body.
|
||||
|
||||
### Requirement 3: Environment Variable Configuration
|
||||
|
||||
**User Story:** As a system administrator, I want CARD API credentials and settings stored in environment variables following the existing pattern, so that configuration is consistent and secrets are not committed to source control.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Dashboard SHALL read the following environment variables for CARD API configuration: `CARD_API_URL` (base URL), `CARD_API_USER` (service account username), `CARD_API_PASS` (service account password), and `CARD_SKIP_TLS` (TLS verification toggle).
|
||||
2. THE Dashboard SHALL document all CARD environment variables in `backend/.env.example` with descriptive comments matching the existing documentation style.
|
||||
3. WHEN any of `CARD_API_URL`, `CARD_API_USER`, or `CARD_API_PASS` is missing or empty, THE CARD_Helper SHALL treat the integration as unconfigured and report `isConfigured` as `false`.
|
||||
4. THE Dashboard SHALL treat `CARD_SKIP_TLS` as optional, defaulting to `false` when not set.
|
||||
|
||||
### Requirement 4: CARD API Proxy Routes
|
||||
|
||||
**User Story:** As a dashboard user, I want backend routes that proxy specific CARD API operations, so that the frontend can trigger CARD actions without exposing API credentials to the browser.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE CARD_Route SHALL export a factory function `createCardApiRouter(db, requireAuth)` that returns an Express Router, following the existing route module pattern.
|
||||
2. THE CARD_Route SHALL protect all endpoints with `requireAuth(db)` for session validation and `requireGroup('Admin', 'Standard_User')` for role-based access.
|
||||
3. THE CARD_Route SHALL expose a `GET /api/card/status` endpoint that returns `{ configured: boolean }` indicating whether the CARD API integration is configured.
|
||||
4. THE CARD_Route SHALL expose a `GET /api/card/teams` endpoint that proxies the CARD_API `GET /api/v1/teams` endpoint and returns the list of CARD teams to the client.
|
||||
5. THE CARD_Route SHALL expose a `GET /api/card/teams/:teamName/assets` endpoint that proxies the CARD_API `GET /api/v1/team/{teamName}/assets` endpoint, accepting `disposition`, `page`, and `page_size` query parameters.
|
||||
6. WHEN the `page_size` query parameter is not provided on the assets endpoint, THE CARD_Route SHALL default to a page size of 50.
|
||||
7. THE CARD_Route SHALL expose a `GET /api/card/owner/:assetId` endpoint that proxies the CARD_API `GET /api/v1/owner/{assetId}` endpoint and returns the Owner_Record including disposition states and the update_token.
|
||||
8. IF `isConfigured` is `false` when a CARD API proxy endpoint is called, THEN THE CARD_Route SHALL return HTTP 503 with `{ error: 'CARD API is not configured.' }`.
|
||||
9. IF the CARD_API returns an error response, THEN THE CARD_Route SHALL return the CARD_API HTTP status code and a JSON error body containing the upstream error message.
|
||||
10. THE CARD_Route SHALL be mounted at the `/api/card` path prefix in `server.js`.
|
||||
|
||||
### Requirement 5: CARD Asset Mutation Actions
|
||||
|
||||
**User Story:** As a dashboard user, I want to confirm, decline, or redirect CARD assets directly from the queue, so that I can process CARD workflow findings without leaving the dashboard.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE CARD_Route SHALL expose a `POST /api/card/queue/:queueItemId/confirm` endpoint that confirms an asset to a specified team via the CARD_API.
|
||||
2. THE CARD_Route SHALL expose a `POST /api/card/queue/:queueItemId/decline` endpoint that declines an asset from a specified team via the CARD_API.
|
||||
3. THE CARD_Route SHALL expose a `POST /api/card/queue/:queueItemId/redirect` endpoint that redirects an asset from one team to another team via the CARD_API.
|
||||
4. WHEN any mutation endpoint is called, THE CARD_Route SHALL verify that the queue item exists, belongs to the requesting user, has `workflow_type = 'CARD'`, and has `status = 'pending'`.
|
||||
5. IF the queue item does not exist, does not belong to the user, or is not a CARD workflow item, THEN THE CARD_Route SHALL return HTTP 404 with `{ error: 'Queue item not found.' }`.
|
||||
6. IF the queue item status is not `'pending'`, THEN THE CARD_Route SHALL return HTTP 400 with `{ error: 'Only pending queue items can be executed.' }`.
|
||||
7. WHEN a mutation endpoint is called, THE CARD_Route SHALL first call `GET /api/v1/owner/{assetId}` to retrieve the current update_token, then use that update_token in the subsequent mutation call, making the two-step flow transparent to the frontend.
|
||||
8. WHEN the confirm endpoint is called, THE CARD_Route SHALL send `POST /api/v2/owner/{assetId}/confirm?update_token={token}&comment={comment}` with body `{ "name": "TEAM-NAME" }` to the CARD_API.
|
||||
9. WHEN the decline endpoint is called, THE CARD_Route SHALL send `POST /api/v2/owner/{assetId}/decline?update_token={token}&comment={comment}` with body `{ "name": "TEAM-NAME" }` to the CARD_API.
|
||||
10. WHEN the redirect endpoint is called, THE CARD_Route SHALL send `POST /api/v2/owner/{assetId}/{fromTeam}/redirect?update_token={token}` with body `{ "name": "TO-TEAM-NAME" }` to the CARD_API, where `fromTeam` is a path parameter and the destination team is in the request body.
|
||||
11. THE confirm endpoint SHALL accept a request body containing `teamName` (string, required), `comment` (string, optional), and `assetId` (string, required).
|
||||
12. THE decline endpoint SHALL accept a request body containing `teamName` (string, required), `comment` (string, optional), and `assetId` (string, required).
|
||||
13. THE redirect endpoint SHALL accept a request body containing `fromTeam` (string, required), `toTeam` (string, required), and `assetId` (string, required).
|
||||
14. WHEN the CARD_API mutation call succeeds, THE CARD_Route SHALL update the queue item status to `'complete'` and return the CARD_API response to the client.
|
||||
15. IF the CARD_API mutation call fails, THEN THE CARD_Route SHALL leave the queue item status as `'pending'` and return the error to the client.
|
||||
|
||||
### Requirement 6: Frontend CARD Action UI
|
||||
|
||||
**User Story:** As a dashboard user, I want specific Confirm, Decline, and Redirect action buttons on CARD queue items, so that I can perform the correct CARD operation for each finding.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a CARD Queue_Item is displayed in the Ivanti Queue panel, THE Dashboard SHALL render three action buttons labeled "Confirm", "Decline", and "Redirect" on pending CARD items.
|
||||
2. WHEN the user clicks the "Confirm" button, THE Dashboard SHALL display a form with a team selection dropdown (populated from the `/api/card/teams` endpoint) and an optional comment text field, then send a `POST` request to `/api/card/queue/:queueItemId/confirm` with the selected team name, comment, and asset ID.
|
||||
3. WHEN the user clicks the "Decline" button, THE Dashboard SHALL display a form with a team selection dropdown and an optional comment text field, then send a `POST` request to `/api/card/queue/:queueItemId/decline` with the selected team name, comment, and asset ID.
|
||||
4. WHEN the user clicks the "Redirect" button, THE Dashboard SHALL display a form with a "From Team" dropdown and a "To Team" dropdown (both populated from the `/api/card/teams` endpoint), then send a `POST` request to `/api/card/queue/:queueItemId/redirect` with the from team, to team, and asset ID.
|
||||
5. WHILE a CARD action request is in flight, THE Dashboard SHALL disable the action buttons and display a loading indicator on the affected queue item.
|
||||
6. WHEN the CARD action request succeeds, THE Dashboard SHALL update the queue item status to `'complete'` in the local UI state without requiring a full queue refresh.
|
||||
7. IF the CARD action request fails, THEN THE Dashboard SHALL display the error message returned by the backend in an inline error indicator on the affected queue item.
|
||||
8. WHEN the CARD API is not configured (status endpoint returns `configured: false`), THE Dashboard SHALL disable CARD action buttons and display a tooltip indicating the integration is not configured.
|
||||
9. THE Dashboard SHALL cache the teams list from `/api/card/teams` for the duration of the browser session to avoid redundant API calls.
|
||||
|
||||
### Requirement 7: Asset Search UI
|
||||
|
||||
**User Story:** As a dashboard user, I want to search CARD for assets by team and disposition, so that I can find Granite IDs when assets get reassigned.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Dashboard SHALL provide an asset search interface accessible from the Ivanti Queue page.
|
||||
2. THE asset search interface SHALL include a team selection dropdown (populated from the `/api/card/teams` endpoint) and a disposition filter dropdown with options: `confirmed`, `unconfirmed`, `declined`, `candidate`.
|
||||
3. WHEN the user initiates a search, THE Dashboard SHALL send a `GET` request to `/api/card/teams/:teamName/assets` with the selected disposition and `page_size=50`.
|
||||
4. WHEN the first page of results is returned, THE Dashboard SHALL display the total asset count and render the first page of results in a table.
|
||||
5. WHEN the total asset count exceeds the page size, THE Dashboard SHALL provide pagination controls to navigate through additional pages by sending subsequent requests with incremented `page` parameters.
|
||||
6. THE asset search results table SHALL display the Asset_ID and any other identifying fields returned by the CARD_API that help the user locate the correct Granite ID.
|
||||
7. IF the asset search request fails, THEN THE Dashboard SHALL display the error message returned by the backend in the search results area.
|
||||
|
||||
### Requirement 8: Error Handling and Resilience
|
||||
|
||||
**User Story:** As a dashboard user, I want clear error feedback when CARD API operations fail, so that I can understand what went wrong and take corrective action.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. IF the CARD_API is unreachable or the request times out, THEN THE CARD_Helper SHALL reject the Promise with an error message that includes the HTTP method, URL path, and failure reason.
|
||||
2. IF the token acquisition endpoint returns invalid or unparseable JSON, THEN THE Token_Manager SHALL reject the Promise with a descriptive error message indicating a token parse failure.
|
||||
3. IF the token acquisition endpoint returns HTTP 403, THEN THE CARD_Route SHALL return HTTP 403 with `{ error: 'CARD access denied. The service account may not be onboarded with the CARD team.' }`.
|
||||
4. IF the token acquisition endpoint returns HTTP 401, THEN THE CARD_Route SHALL return HTTP 401 with `{ error: 'CARD authorization failed. Check service account credentials.' }`.
|
||||
5. IF the token acquisition endpoint returns HTTP 525, THEN THE CARD_Route SHALL return HTTP 502 with `{ error: 'CARD LDAP error. The service account may not be provisioned correctly.' }`.
|
||||
6. IF a CARD_API call returns HTTP 401, THEN THE CARD_Route SHALL return HTTP 401 with `{ error: 'CARD token expired or invalid. The request has been retried once automatically.' }`.
|
||||
7. IF a CARD_API call returns HTTP 403, THEN THE CARD_Route SHALL return HTTP 403 with `{ error: 'Insufficient CARD permissions for this operation.' }`.
|
||||
8. THE CARD_Route SHALL catch all unhandled errors from CARD_Helper calls and return HTTP 502 with `{ error: 'CARD API request failed.', details: <error message> }`.
|
||||
9. THE CARD_Route SHALL log all CARD API errors to the server console with the prefix `[card-api]` for consistent log filtering.
|
||||
10. IF the CARD_Helper is not configured and a proxy endpoint is called, THEN THE CARD_Route SHALL return HTTP 503 with a message indicating which environment variables are missing.
|
||||
|
||||
### Requirement 9: Audit Logging for CARD Actions
|
||||
|
||||
**User Story:** As an administrator, I want all CARD API actions logged in the audit trail, so that I can review what CARD operations were performed and by whom.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a CARD confirm action is executed through the dashboard, THE Audit_Logger SHALL record an entry with `action: 'card_confirm'`, `entityType: 'ivanti_todo_queue'`, the queue item ID as `entityId`, and `details` containing the asset ID, team name, comment, and CARD_API response status.
|
||||
2. WHEN a CARD decline action is executed through the dashboard, THE Audit_Logger SHALL record an entry with `action: 'card_decline'`, `entityType: 'ivanti_todo_queue'`, the queue item ID as `entityId`, and `details` containing the asset ID, team name, comment, and CARD_API response status.
|
||||
3. WHEN a CARD redirect action is executed through the dashboard, THE Audit_Logger SHALL record an entry with `action: 'card_redirect'`, `entityType: 'ivanti_todo_queue'`, the queue item ID as `entityId`, and `details` containing the asset ID, from team, to team, and CARD_API response status.
|
||||
4. WHEN a CARD asset search is executed through the dashboard, THE Audit_Logger SHALL record an entry with `action: 'card_search'`, `entityType: 'card_asset'`, `entityId` set to the team name, and `details` containing the disposition filter and result count.
|
||||
5. WHEN a CARD API action fails, THE Audit_Logger SHALL record an entry with `action: 'card_action_failed'`, `entityType: 'ivanti_todo_queue'`, the queue item ID as `entityId`, and `details` containing the action type, asset ID, error message, and CARD_API response status.
|
||||
6. THE Audit_Logger SHALL record the requesting user's `userId`, `username`, and `ipAddress` on all CARD audit entries.
|
||||
7. THE Audit_Logger SHALL use fire-and-forget semantics for CARD audit entries, matching the existing audit logging pattern.
|
||||
Reference in New Issue
Block a user