diff --git a/deepfence_frontend/apps/dashboard/api-spec.json b/deepfence_frontend/apps/dashboard/api-spec.json index 404c5e3210..c895ecb530 100644 --- a/deepfence_frontend/apps/dashboard/api-spec.json +++ b/deepfence_frontend/apps/dashboard/api-spec.json @@ -283,6 +283,53 @@ "security": [{ "bearer_token": [] }] } }, + "/deepfence/cloud-node/providers/list": { + "post": { + "tags": ["Cloud Nodes"], + "summary": "List Cloud Node Providers", + "description": "List Cloud Node Providers registered with the console", + "operationId": "listCloudProviders", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelCloudNodeProvidersListResp" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsBadRequestResponse" } + } + } + }, + "401": { "description": "Unauthorized" }, + "403": { "description": "Forbidden" }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ApiDocsFailureResponse" } + } + } + } + }, + "security": [{ "bearer_token": [] }] + } + }, "/deepfence/controls/agent": { "post": { "tags": ["Controls"], @@ -4185,6 +4232,17 @@ "trail_name": { "type": "string" } } }, + "ModelCloudNodeProvidersListResp": { + "required": ["providers"], + "type": "object", + "properties": { + "providers": { + "type": "array", + "items": { "type": "string" }, + "nullable": true + } + } + }, "ModelCompliance": { "required": [ "test_category", @@ -4451,7 +4509,8 @@ "node_name", "node_type", "scan_id", - "malwares" + "malwares", + "severity_counts" ], "type": "object", "properties": { @@ -4467,7 +4526,12 @@ "node_id": { "type": "string" }, "node_name": { "type": "string" }, "node_type": { "type": "string" }, - "scan_id": { "type": "string" } + "scan_id": { "type": "string" }, + "severity_counts": { + "type": "object", + "additionalProperties": { "type": "integer" }, + "nullable": true + } } }, "ModelMalwareScanTriggerReq": { @@ -4616,9 +4680,11 @@ } }, "ModelScanInfo": { - "required": ["scan_id", "status", "updated_at"], + "required": ["scan_id", "status", "updated_at", "node_id", "node_type"], "type": "object", "properties": { + "node_id": { "type": "string" }, + "node_type": { "type": "string" }, "scan_id": { "type": "string" }, "status": { "type": "string" }, "updated_at": { "type": "integer", "format": "int64" } @@ -4652,14 +4718,13 @@ "window": { "$ref": "#/components/schemas/ModelFetchWindow" } } }, - "ModelScanStatus": { "type": "string" }, "ModelScanStatusResp": { "required": ["statuses"], "type": "object", "properties": { "statuses": { "type": "object", - "additionalProperties": { "$ref": "#/components/schemas/ModelScanStatus" }, + "additionalProperties": { "$ref": "#/components/schemas/ModelScanInfo" }, "nullable": true } } diff --git a/deepfence_frontend/apps/dashboard/openapitools.json b/deepfence_frontend/apps/dashboard/openapitools.json index 0c81a9fcf3..1b6f7c14b1 100644 --- a/deepfence_frontend/apps/dashboard/openapitools.json +++ b/deepfence_frontend/apps/dashboard/openapitools.json @@ -2,7 +2,7 @@ "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.2.1", + "version": "6.3.0", "generators": { "deepfence-server": { "generatorName": "typescript-fetch", diff --git a/deepfence_frontend/apps/dashboard/src/api/api.ts b/deepfence_frontend/apps/dashboard/src/api/api.ts index 20c28f2aa1..987bc51ddc 100644 --- a/deepfence_frontend/apps/dashboard/src/api/api.ts +++ b/deepfence_frontend/apps/dashboard/src/api/api.ts @@ -40,23 +40,18 @@ export function getTopologyApiClient() { const topologyApi = new TopologyApi(configuration); return { getHostsTopologyGraph: topologyApi.getHostsTopologyGraph.bind(topologyApi), + getKubernetesTopologyGraph: topologyApi.getKubernetesTopologyGraph.bind(topologyApi), }; } -export function getCloudNodesApi() { +export function getCloudNodesApiClient() { const cloudNodesApi = new CloudNodesApi(configuration); return { listCloudNodeAccount: cloudNodesApi.listCloudNodeAccount.bind(cloudNodesApi), }; } -export function getRegistriesApi() { - const registriesApi = new RegistryApi(configuration); - return { - addRegistry: registriesApi.addRegistry.bind(registriesApi), - }; -} -export function vulnerabilityScanApiClient() { +export function getVulnerabilityApiClient() { const vulnerabilityApi = new VulnerabilityApi(configuration); return { startVulnerabilityScan: @@ -68,7 +63,7 @@ export function vulnerabilityScanApiClient() { }; } -export function secretScanApiClient() { +export function getSecretApiClient() { const secretApi = new SecretScanApi(configuration); return { startSecretScan: secretApi.startSecretScan.bind(secretApi), @@ -77,7 +72,7 @@ export function secretScanApiClient() { }; } -export function complianceScanApiClient() { +export function getComplianceApiClient() { const complianceApi = new ComplianceApi(configuration); return { startComplianceScan: complianceApi.startComplianceScan.bind(complianceApi), @@ -85,3 +80,11 @@ export function complianceScanApiClient() { resultComplianceScan: complianceApi.resultsComplianceScan.bind(complianceApi), }; } + +export function getRegistriesApiClient() { + const registriesApi = new RegistryApi(configuration); + return { + listRegistries: registriesApi.listRegistry.bind(registriesApi), + addRegistry: registriesApi.addRegistry.bind(registriesApi), + }; +} diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES index f27d6d93c5..eea63d706b 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES +++ b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/FILES @@ -52,6 +52,7 @@ models/ModelCloudNodeAccountRegisterRespData.ts models/ModelCloudNodeAccountsListReq.ts models/ModelCloudNodeAccountsListResp.ts models/ModelCloudNodeCloudtrailTrail.ts +models/ModelCloudNodeProvidersListResp.ts models/ModelCompliance.ts models/ModelComplianceScanResult.ts models/ModelComplianceScanTriggerReq.ts diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/VERSION b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/VERSION index 0df17dd0f6..e7e42a4b58 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/VERSION +++ b/deepfence_frontend/apps/dashboard/src/api/generated/.openapi-generator/VERSION @@ -1 +1 @@ -6.2.1 \ No newline at end of file +6.3.0 \ No newline at end of file diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/apis/CloudNodesApi.ts b/deepfence_frontend/apps/dashboard/src/api/generated/apis/CloudNodesApi.ts index 6153d43dc0..45538490db 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/apis/CloudNodesApi.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/apis/CloudNodesApi.ts @@ -21,6 +21,7 @@ import type { ModelCloudNodeAccountRegisterResp, ModelCloudNodeAccountsListReq, ModelCloudNodeAccountsListResp, + ModelCloudNodeProvidersListResp, } from '../models'; import { ApiDocsBadRequestResponseFromJSON, @@ -35,6 +36,8 @@ import { ModelCloudNodeAccountsListReqToJSON, ModelCloudNodeAccountsListRespFromJSON, ModelCloudNodeAccountsListRespToJSON, + ModelCloudNodeProvidersListRespFromJSON, + ModelCloudNodeProvidersListRespToJSON, } from '../models'; export interface ListCloudNodeAccountRequest { @@ -68,6 +71,21 @@ export interface CloudNodesApiInterface { */ listCloudNodeAccount(requestParameters: ListCloudNodeAccountRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + /** + * List Cloud Node Providers registered with the console + * @summary List Cloud Node Providers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CloudNodesApiInterface + */ + listCloudProvidersRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * List Cloud Node Providers registered with the console + * List Cloud Node Providers + */ + listCloudProviders(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + /** * Register Cloud Node Account and return any pending compliance scans from console * @summary Register Cloud Node Account @@ -130,6 +148,42 @@ export class CloudNodesApi extends runtime.BaseAPI implements CloudNodesApiInter return await response.value(); } + /** + * List Cloud Node Providers registered with the console + * List Cloud Node Providers + */ + async listCloudProvidersRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("bearer_token", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + const response = await this.request({ + path: `/deepfence/cloud-node/providers/list`, + method: 'POST', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => ModelCloudNodeProvidersListRespFromJSON(jsonValue)); + } + + /** + * List Cloud Node Providers registered with the console + * List Cloud Node Providers + */ + async listCloudProviders(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listCloudProvidersRaw(initOverrides); + return await response.value(); + } + /** * Register Cloud Node Account and return any pending compliance scans from console * Register Cloud Node Account diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelCloudNodeProvidersListResp.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelCloudNodeProvidersListResp.ts new file mode 100644 index 0000000000..bee849d863 --- /dev/null +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelCloudNodeProvidersListResp.ts @@ -0,0 +1,66 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Deepfence ThreatMapper + * Deepfence Runtime API provides programmatic control over Deepfence microservice securing your container, kubernetes and cloud deployments. The API abstracts away underlying infrastructure details like cloud provider, container distros, container orchestrator and type of deployment. This is one uniform API to manage and control security alerts, policies and response to alerts for microservices running anywhere i.e. managed pure greenfield container deployments or a mix of containers, VMs and serverless paradigms like AWS Fargate. + * + * The version of the OpenAPI document: 2.0.0 + * Contact: community@deepfence.io + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ModelCloudNodeProvidersListResp + */ +export interface ModelCloudNodeProvidersListResp { + /** + * + * @type {Array} + * @memberof ModelCloudNodeProvidersListResp + */ + providers: Array | null; +} + +/** + * Check if a given object implements the ModelCloudNodeProvidersListResp interface. + */ +export function instanceOfModelCloudNodeProvidersListResp(value: object): boolean { + let isInstance = true; + isInstance = isInstance && "providers" in value; + + return isInstance; +} + +export function ModelCloudNodeProvidersListRespFromJSON(json: any): ModelCloudNodeProvidersListResp { + return ModelCloudNodeProvidersListRespFromJSONTyped(json, false); +} + +export function ModelCloudNodeProvidersListRespFromJSONTyped(json: any, ignoreDiscriminator: boolean): ModelCloudNodeProvidersListResp { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'providers': json['providers'], + }; +} + +export function ModelCloudNodeProvidersListRespToJSON(value?: ModelCloudNodeProvidersListResp | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'providers': value.providers, + }; +} + diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelMalwareScanResult.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelMalwareScanResult.ts index 9e99359a83..7e13b4e1ce 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelMalwareScanResult.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelMalwareScanResult.ts @@ -80,6 +80,12 @@ export interface ModelMalwareScanResult { * @memberof ModelMalwareScanResult */ scan_id: string; + /** + * + * @type {{ [key: string]: number; }} + * @memberof ModelMalwareScanResult + */ + severity_counts: { [key: string]: number; } | null; } /** @@ -96,6 +102,7 @@ export function instanceOfModelMalwareScanResult(value: object): boolean { isInstance = isInstance && "node_name" in value; isInstance = isInstance && "node_type" in value; isInstance = isInstance && "scan_id" in value; + isInstance = isInstance && "severity_counts" in value; return isInstance; } @@ -119,6 +126,7 @@ export function ModelMalwareScanResultFromJSONTyped(json: any, ignoreDiscriminat 'node_name': json['node_name'], 'node_type': json['node_type'], 'scan_id': json['scan_id'], + 'severity_counts': json['severity_counts'], }; } @@ -140,6 +148,7 @@ export function ModelMalwareScanResultToJSON(value?: ModelMalwareScanResult | nu 'node_name': value.node_name, 'node_type': value.node_type, 'scan_id': value.scan_id, + 'severity_counts': value.severity_counts, }; } diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanInfo.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanInfo.ts index eb8aec189f..270fc1b53c 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanInfo.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanInfo.ts @@ -19,6 +19,18 @@ import { exists, mapValues } from '../runtime'; * @interface ModelScanInfo */ export interface ModelScanInfo { + /** + * + * @type {string} + * @memberof ModelScanInfo + */ + node_id: string; + /** + * + * @type {string} + * @memberof ModelScanInfo + */ + node_type: string; /** * * @type {string} @@ -44,6 +56,8 @@ export interface ModelScanInfo { */ export function instanceOfModelScanInfo(value: object): boolean { let isInstance = true; + isInstance = isInstance && "node_id" in value; + isInstance = isInstance && "node_type" in value; isInstance = isInstance && "scan_id" in value; isInstance = isInstance && "status" in value; isInstance = isInstance && "updated_at" in value; @@ -61,6 +75,8 @@ export function ModelScanInfoFromJSONTyped(json: any, ignoreDiscriminator: boole } return { + 'node_id': json['node_id'], + 'node_type': json['node_type'], 'scan_id': json['scan_id'], 'status': json['status'], 'updated_at': json['updated_at'], @@ -76,6 +92,8 @@ export function ModelScanInfoToJSON(value?: ModelScanInfo | null): any { } return { + 'node_id': value.node_id, + 'node_type': value.node_type, 'scan_id': value.scan_id, 'status': value.status, 'updated_at': value.updated_at, diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanStatusResp.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanStatusResp.ts index bdf1327261..b7b548234d 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanStatusResp.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/ModelScanStatusResp.ts @@ -13,6 +13,13 @@ */ import { exists, mapValues } from '../runtime'; +import type { ModelScanInfo } from './ModelScanInfo'; +import { + ModelScanInfoFromJSON, + ModelScanInfoFromJSONTyped, + ModelScanInfoToJSON, +} from './ModelScanInfo'; + /** * * @export @@ -21,10 +28,10 @@ import { exists, mapValues } from '../runtime'; export interface ModelScanStatusResp { /** * - * @type {{ [key: string]: string; }} + * @type {{ [key: string]: ModelScanInfo; }} * @memberof ModelScanStatusResp */ - statuses: { [key: string]: string; } | null; + statuses: { [key: string]: ModelScanInfo; } | null; } /** @@ -47,7 +54,7 @@ export function ModelScanStatusRespFromJSONTyped(json: any, ignoreDiscriminator: } return { - 'statuses': json['statuses'], + 'statuses': (json['statuses'] === null ? null : mapValues(json['statuses'], ModelScanInfoFromJSON)), }; } @@ -60,7 +67,7 @@ export function ModelScanStatusRespToJSON(value?: ModelScanStatusResp | null): a } return { - 'statuses': value.statuses, + 'statuses': (value.statuses === null ? null : mapValues(value.statuses, ModelScanInfoToJSON)), }; } diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts index b5ca59f0b0..79591986d4 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/models/index.ts @@ -35,6 +35,7 @@ export * from './ModelCloudNodeAccountRegisterRespData'; export * from './ModelCloudNodeAccountsListReq'; export * from './ModelCloudNodeAccountsListResp'; export * from './ModelCloudNodeCloudtrailTrail'; +export * from './ModelCloudNodeProvidersListResp'; export * from './ModelCompliance'; export * from './ModelComplianceScanResult'; export * from './ModelComplianceScanTriggerReq'; diff --git a/deepfence_frontend/apps/dashboard/src/api/generated/runtime.ts b/deepfence_frontend/apps/dashboard/src/api/generated/runtime.ts index d1c0efe5f6..d36905affc 100644 --- a/deepfence_frontend/apps/dashboard/src/api/generated/runtime.ts +++ b/deepfence_frontend/apps/dashboard/src/api/generated/runtime.ts @@ -146,7 +146,7 @@ export class BaseAPI { credentials: this.configuration.credentials, }; - const overridedInit: RequestInit = { + const overriddenInit: RequestInit = { ...initParams, ...(await initOverrideFn({ init: initParams, @@ -155,13 +155,13 @@ export class BaseAPI { }; const init: RequestInit = { - ...overridedInit, + ...overriddenInit, body: - isFormData(overridedInit.body) || - overridedInit.body instanceof URLSearchParams || - isBlob(overridedInit.body) - ? overridedInit.body - : JSON.stringify(overridedInit.body), + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ? overriddenInit.body + : JSON.stringify(overriddenInit.body), }; return { url, init }; @@ -177,7 +177,7 @@ export class BaseAPI { }) || fetchParams; } } - let response = undefined; + let response: Response | undefined = undefined; try { response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); } catch (e) { diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/DockerRegistryConnector.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/DockerRegistryConnector.tsx index 7526a2942d..818b024734 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/DockerRegistryConnector.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/DockerRegistryConnector.tsx @@ -1,7 +1,7 @@ import { ActionFunctionArgs, Form, redirect, useActionData } from 'react-router-dom'; import { Button } from 'ui-components'; -import { getRegistriesApi } from '@/api/api'; +import { getRegistriesApiClient } from '@/api/api'; import { ApiDocsBadRequestResponse } from '@/api/generated'; import { ConnectorHeader } from '@/features/onboard/components/ConnectorHeader'; import { DockerConnectionForm } from '@/features/onboard/components/connectors/registries/DockerConnectionForm'; @@ -17,7 +17,7 @@ const action = async ({ request }: ActionFunctionArgs): Promise { + prev[curr] = result.statuses?.[curr].status ?? ''; + return prev; + }, {} as Record), }; } diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanConfigure.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanConfigure.tsx index 7b7322c4a2..18363e38d6 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanConfigure.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanConfigure.tsx @@ -1,7 +1,7 @@ import { ActionFunctionArgs, Form, generatePath, redirect } from 'react-router-dom'; import { Button } from 'ui-components'; -import { secretScanApiClient } from '@/api/api'; +import { getSecretApiClient } from '@/api/api'; import { ApiDocsBadRequestResponse } from '@/api/generated'; import { ModelScanTriggerNodeTypeEnum } from '@/api/generated/models/ModelScanTrigger'; import { ConnectorHeader } from '@/features/onboard/components/ConnectorHeader'; @@ -23,7 +23,7 @@ const action = async ({ const nodeIdArray = nodeIds?.split(','); const r = await makeRequest({ - apiFunction: secretScanApiClient().startSecretScan, + apiFunction: getSecretApiClient().startSecretScan, apiArgs: [ { modelSecretScanTriggerReq: { diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanSummary.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanSummary.tsx index 11487e6568..6322910089 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanSummary.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/SecretScanSummary.tsx @@ -3,7 +3,7 @@ import { Suspense } from 'react'; import { Await, Link, LoaderFunctionArgs, useLoaderData } from 'react-router-dom'; import { Card, CircleSpinner, Typography } from 'ui-components'; -import { secretScanApiClient } from '@/api/api'; +import { getSecretApiClient } from '@/api/api'; import { ModelSecretScanResult } from '@/api/generated/models/ModelSecretScanResult'; import LogoLinux from '@/assets/logo-linux.svg'; import { ConnectorHeader } from '@/features/onboard/components/ConnectorHeader'; @@ -48,7 +48,7 @@ export type LoaderDataType = { async function getScanSummary(scanIds: string): Promise { const bulkRequest = scanIds.split(',').map((scanId) => { return makeRequest({ - apiFunction: secretScanApiClient().resultSecretScan, + apiFunction: getSecretApiClient().resultSecretScan, apiArgs: [ { modelScanResultsReq: { diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanConfigure.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanConfigure.tsx index e2805ffa38..91e274facd 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanConfigure.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanConfigure.tsx @@ -9,7 +9,7 @@ import { } from 'react-router-dom'; import { Button, Checkbox, Switch, Typography } from 'ui-components'; -import { vulnerabilityScanApiClient } from '@/api/api'; +import { getVulnerabilityApiClient } from '@/api/api'; import { ApiDocsBadRequestResponse } from '@/api/generated'; import { ModelScanTriggerNodeTypeEnum } from '@/api/generated/models/ModelScanTrigger'; import { ConnectorHeader } from '@/features/onboard/components/ConnectorHeader'; @@ -35,7 +35,7 @@ const action = async ({ const body = Object.fromEntries(formData); const r = await makeRequest({ - apiFunction: vulnerabilityScanApiClient().startVulnerabilityScan, + apiFunction: getVulnerabilityApiClient().startVulnerabilityScan, apiArgs: [ { modelVulnerabilityScanTriggerReq: { diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanSummary.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanSummary.tsx index 668fc80a68..c3d2abe01b 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanSummary.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/VulnerabilityScanSummary.tsx @@ -3,7 +3,7 @@ import { Suspense } from 'react'; import { Await, Link, LoaderFunctionArgs, useLoaderData } from 'react-router-dom'; import { Card, CircleSpinner, Typography } from 'ui-components'; -import { vulnerabilityScanApiClient } from '@/api/api'; +import { getVulnerabilityApiClient } from '@/api/api'; import { ModelVulnerabilityScanResult } from '@/api/generated/models/ModelVulnerabilityScanResult'; import LogoLinux from '@/assets/logo-linux.svg'; import { ConnectorHeader } from '@/features/onboard/components/ConnectorHeader'; @@ -48,7 +48,7 @@ export type LoaderDataType = { async function getScanSummary(scanIds: string): Promise { const bulkRequest = scanIds.split(',').map((scanId) => { return makeRequest({ - apiFunction: vulnerabilityScanApiClient().resultVulnerabilityScan, + apiFunction: getVulnerabilityApiClient().resultVulnerabilityScan, apiArgs: [ { modelScanResultsReq: { diff --git a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/connectors/MyConnectors.tsx b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/connectors/MyConnectors.tsx index 5b5af3ac50..3e7c9662b3 100644 --- a/deepfence_frontend/apps/dashboard/src/features/onboard/pages/connectors/MyConnectors.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/onboard/pages/connectors/MyConnectors.tsx @@ -1,6 +1,11 @@ import { Suspense, useMemo, useRef, useState } from 'react'; -import { HiChevronDown, HiChevronRight, HiCubeTransparent } from 'react-icons/hi'; -import { Await, generatePath, useLoaderData } from 'react-router-dom'; +import { + HiChevronDown, + HiChevronRight, + HiCubeTransparent, + HiRefresh, +} from 'react-icons/hi'; +import { Await, generatePath, useLoaderData, useRevalidator } from 'react-router-dom'; import { Button, createColumnHelper, @@ -13,7 +18,11 @@ import { Tabs, } from 'ui-components'; -import { getCloudNodesApi, getTopologyApiClient } from '@/api/api'; +import { + getCloudNodesApiClient, + getRegistriesApiClient, + getTopologyApiClient, +} from '@/api/api'; import { DFLink } from '@/components/DFLink'; import { NoConnectors } from '@/features/onboard/components/connectors/NoConnectors'; import { connectorLayoutTabs } from '@/features/onboard/layouts/ConnectorsLayout'; @@ -23,11 +32,17 @@ import { usePageNavigation } from '@/utils/usePageNavigation'; interface ConnectionNode { id: string; - apiId: string; - apiType: string; + // url friendly id of the node + urlId: string; + // url friendly api type of the node + urlType: string; + // applies only to the parent node count?: number; + // account type to display in the table accountType: string; + // connection method to display in the table connectionMethod?: string; + // account id to display in the table accountId?: string; active?: boolean; connections?: ConnectionNode[]; @@ -39,7 +54,7 @@ type LoaderData = { async function getConnectorsData(): Promise> { const awsResultsPromise = makeRequest({ - apiFunction: getCloudNodesApi().listCloudNodeAccount, + apiFunction: getCloudNodesApiClient().listCloudNodeAccount, apiArgs: [ { modelCloudNodeAccountsListReq: { @@ -67,12 +82,39 @@ async function getConnectorsData(): Promise> { }, ], }); - const [awsResults, hostsResults] = await Promise.all([ - awsResultsPromise, - hostsResultsPromise, - ]); + const kubernetesResultsPromise = makeRequest({ + apiFunction: getTopologyApiClient().getKubernetesTopologyGraph, + apiArgs: [ + { + reportersTopologyFilters: { + cloud_filter: [], + field_filters: { contains_filter: { filter_in: null } }, + host_filter: [], + kubernetes_filter: [], + pod_filter: [], + region_filter: [], + }, + }, + ], + }); + const registriesResultsPromise = makeRequest({ + apiFunction: getRegistriesApiClient().listRegistries, + apiArgs: [], + }); + const [awsResults, hostsResults, kubernetesResults, registriesResults] = + await Promise.all([ + awsResultsPromise, + hostsResultsPromise, + kubernetesResultsPromise, + registriesResultsPromise, + ]); - if (ApiError.isApiError(awsResults) || ApiError.isApiError(hostsResults)) { + if ( + ApiError.isApiError(awsResults) || + ApiError.isApiError(hostsResults) || + ApiError.isApiError(kubernetesResults) || + ApiError.isApiError(registriesResults) + ) { // TODO(manan) handle error cases return []; } @@ -81,16 +123,16 @@ async function getConnectorsData(): Promise> { if (awsResults.total) { data.push({ id: 'aws', - apiId: 'aws', - apiType: 'aws', + urlId: 'aws', + urlType: 'aws', accountType: 'AWS', count: awsResults.total, connections: ( awsResults.cloud_node_accounts_info?.map((result) => ({ id: `aws-${result.node_id}`, - apiId: result.node_id ?? '', + urlId: result.node_id ?? '', accountType: 'AWS', - apiType: 'aws', + urlType: 'aws', connectionMethod: 'Terraform', accountId: result.node_name ?? '-', active: !!result.active, @@ -113,14 +155,14 @@ async function getConnectorsData(): Promise> { if (hosts.length) { data.push({ id: 'hosts', - apiId: 'hosts', - apiType: 'host', + urlId: 'hosts', + urlType: 'host', accountType: 'Linux Hosts', count: hosts.length, connections: hosts.map((host) => ({ id: `hosts-${host.id}`, - apiId: host.id ?? '', - apiType: 'host', + urlId: host.id ?? '', + urlType: 'host', accountType: 'Host', connectionMethod: 'Agent', accountId: host.label ?? host.id ?? '-', @@ -129,6 +171,58 @@ async function getConnectorsData(): Promise> { }); } } + if (kubernetesResults.nodes) { + const clusters = Object.keys(kubernetesResults.nodes) + .map((key) => kubernetesResults.nodes[key]) + .filter((node) => { + return node.type === 'kubernetes_cluster'; + }) + .sort((a, b) => { + return (a.label ?? a.id ?? '').localeCompare(b.label ?? b.id ?? ''); + }); + if (clusters.length) { + data.push({ + id: 'kubernetesCluster', + urlId: 'kubernetes_cluster', + urlType: 'kubernetes_cluster', + accountType: 'Kubernetes Cluster', + count: clusters.length, + connections: clusters.map((cluster) => ({ + id: `kubernetesCluster-${cluster.id}`, + urlId: cluster.id ?? '', + urlType: 'kubernetes_cluster', + accountType: 'Kubernetes Cluster', + connectionMethod: 'Agent', + accountId: cluster.label ?? cluster.id ?? '-', + active: true, + })), + }); + } + } + + if (registriesResults.length === 1) { + data.push({ + id: 'registry', + urlId: 'registry', + urlType: 'registry', + accountType: 'Container Registries', + count: registriesResults.length, + // TODO: fix types for this once added in the API + connections: registriesResults.map((registry: any) => ({ + id: `registry-${registry.ID}`, + urlId: registry.ID ?? '', + urlType: 'registry', + accountType: registry.RegistryType, + connectionMethod: 'Registry', + accountId: + registry?.NonSecret?.docker_hub_namespace ?? + registry.Name ?? + registry.ID ?? + '-', + active: true, + })), + }); + } return data; } @@ -215,6 +309,12 @@ function MyConnectorsTable({ data }: LoaderData) { case 'hosts': nodeText = 'hosts'; break; + case 'kubernetesCluster': + nodeText = 'clusters'; + break; + case 'registry': + nodeText = 'registries'; + break; default: nodeText = 'items'; } @@ -228,8 +328,8 @@ function MyConnectorsTable({ data }: LoaderData) { {rowSelectionState[info.row.original.id] ? ( n.apiId).join(','), + nodeType: info.row.original.urlType, + nodeIds: info.row.original.connections!.map((n) => n.urlId).join(','), })} className="flex items-center" > @@ -240,8 +340,8 @@ function MyConnectorsTable({ data }: LoaderData) { selectedNodesOfSameType.length ? ( n.apiId).join(','), + nodeType: info.row.original.urlType, + nodeIds: selectedNodesOfSameType.map((n) => n.urlId).join(','), })} className="flex items-center" > @@ -279,8 +379,8 @@ function MyConnectorsTable({ data }: LoaderData) { return ( @@ -298,7 +398,7 @@ function MyConnectorsTable({ data }: LoaderData) { } return ( <> - + -
Filter by
- - - );