19 KiB
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) orcard.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.jsmodule 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_queuetable withworkflow_type = 'CARD', representing a finding staged for CARD action. - CARD_Route: The new Express route module at
backend/routes/cardApi.jsthat exposes CARD API operations to the frontend through the backend. - Audit_Logger: The existing
logAudit(db, {...})helper that records state-changing actions to theaudit_logstable. - Auth_Middleware: The existing
requireAuth(db)andrequireGroup(...)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, andcandidate. - 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
- THE CARD_Helper SHALL export an
isConfiguredboolean that istrueonly when all required environment variables (CARD_API_URL,CARD_API_USER,CARD_API_PASS) are present and non-empty. - WHEN
isConfiguredisfalse, THE CARD_Helper SHALL log a warning at module load listing the missing environment variables with the prefix[card-api]. - THE CARD_Helper SHALL use the Node.js built-in
httpsmodule for all HTTP requests to the CARD_API. - 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.
- THE CARD_Helper SHALL set
rejectUnauthorizedtofalseon HTTPS requests when theCARD_SKIP_TLSenvironment variable is set to'true'. - THE CARD_Helper SHALL apply a configurable request timeout defaulting to 15000 milliseconds.
- THE CARD_Helper SHALL return a Promise that resolves with an object containing
status(HTTP status code) andbody(response body string) for each request. - 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
- 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_tokenendpoint with a Basic Auth header containing the base64-encodedCARD_API_USER:CARD_API_PASScredentials. - 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).
- 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.
- 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.
- THE Token_Manager SHALL include the cached Bearer token in the
Authorizationheader of all non-authentication CARD API requests. - 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.
- 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
- 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), andCARD_SKIP_TLS(TLS verification toggle). - THE Dashboard SHALL document all CARD environment variables in
backend/.env.examplewith descriptive comments matching the existing documentation style. - WHEN any of
CARD_API_URL,CARD_API_USER, orCARD_API_PASSis missing or empty, THE CARD_Helper SHALL treat the integration as unconfigured and reportisConfiguredasfalse. - THE Dashboard SHALL treat
CARD_SKIP_TLSas optional, defaulting tofalsewhen 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
- THE CARD_Route SHALL export a factory function
createCardApiRouter(db, requireAuth)that returns an Express Router, following the existing route module pattern. - THE CARD_Route SHALL protect all endpoints with
requireAuth(db)for session validation andrequireGroup('Admin', 'Standard_User')for role-based access. - THE CARD_Route SHALL expose a
GET /api/card/statusendpoint that returns{ configured: boolean }indicating whether the CARD API integration is configured. - THE CARD_Route SHALL expose a
GET /api/card/teamsendpoint that proxies the CARD_APIGET /api/v1/teamsendpoint and returns the list of CARD teams to the client. - THE CARD_Route SHALL expose a
GET /api/card/teams/:teamName/assetsendpoint that proxies the CARD_APIGET /api/v1/team/{teamName}/assetsendpoint, acceptingdisposition,page, andpage_sizequery parameters. - WHEN the
page_sizequery parameter is not provided on the assets endpoint, THE CARD_Route SHALL default to a page size of 50. - THE CARD_Route SHALL expose a
GET /api/card/owner/:assetIdendpoint that proxies the CARD_APIGET /api/v1/owner/{assetId}endpoint and returns the Owner_Record including disposition states and the update_token. - IF
isConfiguredisfalsewhen a CARD API proxy endpoint is called, THEN THE CARD_Route SHALL return HTTP 503 with{ error: 'CARD API is not configured.' }. - 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.
- THE CARD_Route SHALL be mounted at the
/api/cardpath prefix inserver.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
- THE CARD_Route SHALL expose a
POST /api/card/queue/:queueItemId/confirmendpoint that confirms an asset to a specified team via the CARD_API. - THE CARD_Route SHALL expose a
POST /api/card/queue/:queueItemId/declineendpoint that declines an asset from a specified team via the CARD_API. - THE CARD_Route SHALL expose a
POST /api/card/queue/:queueItemId/redirectendpoint that redirects an asset from one team to another team via the CARD_API. - 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 hasstatus = 'pending'. - 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.' }. - 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.' }. - 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. - 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. - 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. - 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, wherefromTeamis a path parameter and the destination team is in the request body. - THE confirm endpoint SHALL accept a request body containing
teamName(string, required),comment(string, optional), andassetId(string, required). - THE decline endpoint SHALL accept a request body containing
teamName(string, required),comment(string, optional), andassetId(string, required). - THE redirect endpoint SHALL accept a request body containing
fromTeam(string, required),toTeam(string, required), andassetId(string, required). - 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. - 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
- 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.
- WHEN the user clicks the "Confirm" button, THE Dashboard SHALL display a form with a team selection dropdown (populated from the
/api/card/teamsendpoint) and an optional comment text field, then send aPOSTrequest to/api/card/queue/:queueItemId/confirmwith the selected team name, comment, and asset ID. - 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
POSTrequest to/api/card/queue/:queueItemId/declinewith the selected team name, comment, and asset ID. - 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/teamsendpoint), then send aPOSTrequest to/api/card/queue/:queueItemId/redirectwith the from team, to team, and asset ID. - 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.
- 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. - 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.
- 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. - THE Dashboard SHALL cache the teams list from
/api/card/teamsfor 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
- THE Dashboard SHALL provide an asset search interface accessible from the Ivanti Queue page.
- THE asset search interface SHALL include a team selection dropdown (populated from the
/api/card/teamsendpoint) and a disposition filter dropdown with options:confirmed,unconfirmed,declined,candidate. - WHEN the user initiates a search, THE Dashboard SHALL send a
GETrequest to/api/card/teams/:teamName/assetswith the selected disposition andpage_size=50. - 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.
- 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
pageparameters. - 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.
- 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
- 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.
- 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.
- 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.' }. - 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.' }. - 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.' }. - 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.' }. - IF a CARD_API call returns HTTP 403, THEN THE CARD_Route SHALL return HTTP 403 with
{ error: 'Insufficient CARD permissions for this operation.' }. - 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> }. - THE CARD_Route SHALL log all CARD API errors to the server console with the prefix
[card-api]for consistent log filtering. - 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
- 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 asentityId, anddetailscontaining the asset ID, team name, comment, and CARD_API response status. - 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 asentityId, anddetailscontaining the asset ID, team name, comment, and CARD_API response status. - 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 asentityId, anddetailscontaining the asset ID, from team, to team, and CARD_API response status. - WHEN a CARD asset search is executed through the dashboard, THE Audit_Logger SHALL record an entry with
action: 'card_search',entityType: 'card_asset',entityIdset to the team name, anddetailscontaining the disposition filter and result count. - 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 asentityId, anddetailscontaining the action type, asset ID, error message, and CARD_API response status. - THE Audit_Logger SHALL record the requesting user's
userId,username, andipAddresson all CARD audit entries. - THE Audit_Logger SHALL use fire-and-forget semantics for CARD audit entries, matching the existing audit logging pattern.