mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Check team config for software UI (#8338)
This commit is contained in:
parent
6d4c885f22
commit
9f20f01e37
@ -13,7 +13,7 @@ interface ITeamsDropdownHeaderProps {
|
||||
router: InjectedRouter;
|
||||
location: {
|
||||
pathname: string;
|
||||
query: { team_id?: string; vulnerable?: boolean };
|
||||
query: { team_id?: string; vulnerable?: string };
|
||||
search: string;
|
||||
};
|
||||
baseClass: string;
|
||||
|
@ -113,6 +113,11 @@ export interface IConfigFormData {
|
||||
transparency_url: string;
|
||||
}
|
||||
|
||||
export interface IConfigFeatures {
|
||||
enable_host_users: boolean;
|
||||
enable_software_inventory: boolean;
|
||||
}
|
||||
|
||||
export interface IConfig {
|
||||
org_info: {
|
||||
org_name: string;
|
||||
@ -154,10 +159,7 @@ export interface IConfig {
|
||||
host_expiry_enabled: boolean;
|
||||
host_expiry_window: number;
|
||||
};
|
||||
features: {
|
||||
enable_host_users: boolean;
|
||||
enable_software_inventory: boolean;
|
||||
};
|
||||
features: IConfigFeatures;
|
||||
agent_options: string;
|
||||
update_interval: {
|
||||
osquery_detail: number;
|
||||
@ -220,3 +222,5 @@ export type IAutomationsConfig = Pick<
|
||||
IConfig,
|
||||
"webhook_settings" | "integrations"
|
||||
>;
|
||||
|
||||
export const CONFIG_DEFAULT_RECENT_VULNERABILITY_MAX_AGE_IN_DAYS = 30;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { IConfigFeatures, IWebhookSettings } from "./config";
|
||||
import enrollSecretInterface, { IEnrollSecret } from "./enroll_secret";
|
||||
import { IIntegrations } from "./integration";
|
||||
import { IWebhookFailingPolicies } from "./webhook";
|
||||
|
||||
export default PropTypes.shape({
|
||||
id: PropTypes.number.isRequired,
|
||||
@ -33,6 +33,7 @@ export interface ITeam extends ITeamSummary {
|
||||
display_text?: string;
|
||||
count?: number;
|
||||
created_at?: string;
|
||||
features?: IConfigFeatures;
|
||||
agent_options?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
@ -42,13 +43,19 @@ export interface ITeam extends ITeamSummary {
|
||||
role?: string; // role value is included when the team is in the context of a user
|
||||
}
|
||||
|
||||
/**
|
||||
* The webhook settings of a team
|
||||
*/
|
||||
export type ITeamWebhookSettings = Pick<
|
||||
IWebhookSettings,
|
||||
"vulnerabilities_webhook" | "failing_policies_webhook"
|
||||
>;
|
||||
|
||||
/**
|
||||
* The integrations and webhook settings of a team
|
||||
*/
|
||||
export interface ITeamAutomationsConfig {
|
||||
webhook_settings: {
|
||||
failing_policies_webhook: IWebhookFailingPolicies;
|
||||
};
|
||||
webhook_settings: ITeamWebhookSettings;
|
||||
integrations: IIntegrations;
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,11 @@ const Homepage = (): JSX.Element => {
|
||||
}
|
||||
);
|
||||
|
||||
const isSoftwareEnabled = config?.features?.enable_software_inventory;
|
||||
const featuresConfig = currentTeam?.id
|
||||
? teams?.find((t) => t.id === currentTeam.id)?.features
|
||||
: config?.features;
|
||||
const isSoftwareEnabled = !!featuresConfig?.enable_software_inventory;
|
||||
|
||||
const SOFTWARE_DEFAULT_SORT_DIRECTION = "desc";
|
||||
const SOFTWARE_DEFAULT_SORT_HEADER = "hosts_count";
|
||||
const SOFTWARE_DEFAULT_PAGE_SIZE = 8;
|
||||
|
@ -61,7 +61,7 @@ const Software = ({
|
||||
) : (
|
||||
<TableContainer
|
||||
columns={tableHeaders}
|
||||
data={software?.software || []}
|
||||
data={(isSoftwareEnabled && software?.software) || []}
|
||||
isLoading={isSoftwareFetching}
|
||||
defaultSortHeader={"hosts_count"}
|
||||
defaultSortDirection={SOFTWARE_DEFAULT_SORT_DIRECTION}
|
||||
@ -89,7 +89,7 @@ const Software = ({
|
||||
) : (
|
||||
<TableContainer
|
||||
columns={tableHeaders}
|
||||
data={software?.software || []}
|
||||
data={(isSoftwareEnabled && software?.software) || []}
|
||||
isLoading={isSoftwareFetching}
|
||||
defaultSortHeader={SOFTWARE_DEFAULT_SORT_HEADER}
|
||||
defaultSortDirection={SOFTWARE_DEFAULT_SORT_DIRECTION}
|
||||
|
@ -9,14 +9,12 @@ import classnames from "classnames";
|
||||
import { pick } from "lodash";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import configAPI from "services/entities/config";
|
||||
import hostAPI from "services/entities/hosts";
|
||||
import queryAPI from "services/entities/queries";
|
||||
import teamAPI, { ILoadTeamsResponse } from "services/entities/teams";
|
||||
import { AppContext } from "context/app";
|
||||
import { PolicyContext } from "context/policy";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { IConfig } from "interfaces/config";
|
||||
import {
|
||||
IHost,
|
||||
IDeviceMappingResponse,
|
||||
@ -97,11 +95,12 @@ const HostDetailsPage = ({
|
||||
}: IHostDetailsProps): JSX.Element => {
|
||||
const hostIdFromURL = parseInt(host_id, 10);
|
||||
const {
|
||||
config,
|
||||
currentUser,
|
||||
isGlobalAdmin,
|
||||
isPremiumTier,
|
||||
isOnlyObserver,
|
||||
isGlobalMaintainer,
|
||||
currentUser,
|
||||
} = useContext(AppContext);
|
||||
const {
|
||||
setLastEditedQueryName,
|
||||
@ -197,14 +196,6 @@ const HostDetailsPage = ({
|
||||
}
|
||||
);
|
||||
|
||||
const { data: features } = useQuery<
|
||||
IConfig,
|
||||
Error,
|
||||
{ enable_host_users: boolean; enable_software_inventory: boolean }
|
||||
>(["config"], () => configAPI.loadAll(), {
|
||||
select: (data: IConfig) => data.features,
|
||||
});
|
||||
|
||||
const refetchExtensions = () => {
|
||||
deviceMapping !== null && refetchDeviceMapping();
|
||||
macadmins !== null && refetchMacadmins();
|
||||
@ -298,6 +289,10 @@ const HostDetailsPage = ({
|
||||
}
|
||||
);
|
||||
|
||||
const featuresConfig = host?.team_id
|
||||
? teams?.find((t) => t.id === host.team_id)?.features
|
||||
: config?.features;
|
||||
|
||||
useEffect(() => {
|
||||
setUsersState(() => {
|
||||
return (
|
||||
@ -603,14 +598,16 @@ const HostDetailsPage = ({
|
||||
usersState={usersState}
|
||||
isLoading={isLoadingHost}
|
||||
onUsersTableSearchChange={onUsersTableSearchChange}
|
||||
hostUsersEnabled={features?.enable_host_users}
|
||||
hostUsersEnabled={featuresConfig?.enable_host_users}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<SoftwareCard
|
||||
isLoading={isLoadingHost}
|
||||
software={hostSoftware}
|
||||
softwareInventoryEnabled={features?.enable_software_inventory}
|
||||
softwareInventoryEnabled={
|
||||
featuresConfig?.enable_software_inventory
|
||||
}
|
||||
deviceType={host?.platform === "darwin" ? "macos" : ""}
|
||||
/>
|
||||
{macadmins && (
|
||||
|
@ -8,9 +8,10 @@ import { PolicyContext } from "context/policy";
|
||||
import { TableContext } from "context/table";
|
||||
import { NotificationContext } from "context/notification";
|
||||
|
||||
import { IAutomationsConfig, IConfig } from "interfaces/config";
|
||||
import { IConfig, IWebhookSettings } from "interfaces/config";
|
||||
import { IIntegrations } from "interfaces/integration";
|
||||
import { IPolicyStats, ILoadAllPoliciesResponse } from "interfaces/policy";
|
||||
import { ITeamAutomationsConfig, ITeamConfig } from "interfaces/team";
|
||||
import { ITeamConfig } from "interfaces/team";
|
||||
|
||||
import PATHS from "router/paths";
|
||||
import configAPI from "services/entities/config";
|
||||
@ -200,9 +201,10 @@ const ManagePolicyPage = ({
|
||||
const toggleShowInheritedPolicies = () =>
|
||||
setShowInheritedPolicies(!showInheritedPolicies);
|
||||
|
||||
const handleUpdateAutomations = async (
|
||||
requestBody: IAutomationsConfig | ITeamAutomationsConfig
|
||||
) => {
|
||||
const handleUpdateAutomations = async (requestBody: {
|
||||
webhook_settings: Pick<IWebhookSettings, "failing_policies_webhook">;
|
||||
integrations: IIntegrations;
|
||||
}) => {
|
||||
setIsUpdatingAutomations(true);
|
||||
try {
|
||||
await (teamId
|
||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { isEmpty, noop, omit } from "lodash";
|
||||
|
||||
import { IAutomationsConfig } from "interfaces/config";
|
||||
import { IAutomationsConfig, IWebhookSettings } from "interfaces/config";
|
||||
import { IIntegration, IIntegrations } from "interfaces/integration";
|
||||
import { IPolicy } from "interfaces/policy";
|
||||
import { ITeamAutomationsConfig } from "interfaces/team";
|
||||
@ -29,7 +29,10 @@ interface IManageAutomationsModalProps {
|
||||
isUpdatingAutomations: boolean;
|
||||
showPreviewPayloadModal: boolean;
|
||||
onExit: () => void;
|
||||
handleSubmit: (formData: IAutomationsConfig | ITeamAutomationsConfig) => void;
|
||||
handleSubmit: (formData: {
|
||||
webhook_settings: Pick<IWebhookSettings, "failing_policies_webhook">;
|
||||
integrations: IIntegrations;
|
||||
}) => void;
|
||||
togglePreviewPayloadModal: () => void;
|
||||
}
|
||||
|
||||
|
@ -11,18 +11,23 @@ import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
import { AppContext } from "context/app";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { IConfig } from "interfaces/config";
|
||||
import {
|
||||
IConfig,
|
||||
CONFIG_DEFAULT_RECENT_VULNERABILITY_MAX_AGE_IN_DAYS,
|
||||
} from "interfaces/config";
|
||||
import {
|
||||
IJiraIntegration,
|
||||
IZendeskIntegration,
|
||||
IIntegration,
|
||||
IIntegrations,
|
||||
} from "interfaces/integration";
|
||||
import { ITeamConfig } from "interfaces/team";
|
||||
import { IWebhookSoftwareVulnerabilities } from "interfaces/webhook"; // @ts-ignore
|
||||
import configAPI from "services/entities/config";
|
||||
import softwareAPI, {
|
||||
ISoftwareResponse,
|
||||
ISoftwareCountResponse,
|
||||
} from "services/entities/software";
|
||||
import teamsAPI, { ILoadTeamResponse } from "services/entities/teams";
|
||||
import {
|
||||
GITHUB_NEW_ISSUE_LINK,
|
||||
VULNERABLE_DROPDOWN_OPTIONS,
|
||||
@ -50,7 +55,7 @@ interface IManageSoftwarePageProps {
|
||||
router: InjectedRouter;
|
||||
location: {
|
||||
pathname: string;
|
||||
query: { vulnerable?: boolean };
|
||||
query: { vulnerable?: string };
|
||||
search: string;
|
||||
};
|
||||
}
|
||||
@ -66,6 +71,11 @@ interface ISoftwareQueryKey {
|
||||
teamId?: number;
|
||||
}
|
||||
|
||||
interface ISoftwareConfigQueryKey {
|
||||
scope: string;
|
||||
teamId?: number;
|
||||
}
|
||||
|
||||
interface ISoftwareAutomations {
|
||||
webhook_settings: {
|
||||
vulnerabilities_webhook: IWebhookSoftwareVulnerabilities;
|
||||
@ -89,9 +99,8 @@ const ManageSoftwarePage = ({
|
||||
}: IManageSoftwarePageProps): JSX.Element => {
|
||||
const {
|
||||
availableTeams,
|
||||
config: globalConfig,
|
||||
currentTeam,
|
||||
isGlobalAdmin,
|
||||
isGlobalMaintainer,
|
||||
isOnGlobalTeam,
|
||||
isPremiumTier,
|
||||
} = useContext(AppContext);
|
||||
@ -99,17 +108,10 @@ const ManageSoftwarePage = ({
|
||||
|
||||
const DEFAULT_SORT_HEADER = isPremiumTier ? "vulnerabilities" : "hosts_count";
|
||||
|
||||
const [isSoftwareEnabled, setIsSoftwareEnabled] = useState(false);
|
||||
const [
|
||||
isVulnerabilityAutomationsEnabled,
|
||||
setIsVulnerabilityAutomationsEnabled,
|
||||
] = useState(false);
|
||||
const [
|
||||
recentVulnerabilityMaxAge,
|
||||
setRecentVulnerabilityMaxAge,
|
||||
] = useState<number>();
|
||||
// TODO: refactor usage of vulnerable query param in accordance with new patterns for query params
|
||||
// and management of URL state
|
||||
const [filterVuln, setFilterVuln] = useState(
|
||||
location?.query?.vulnerable || false
|
||||
location?.query?.vulnerable === "true" || false
|
||||
);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [sortDirection, setSortDirection] = useState<
|
||||
@ -123,44 +125,68 @@ const ManageSoftwarePage = ({
|
||||
const [showPreviewPayloadModal, setShowPreviewPayloadModal] = useState(false);
|
||||
const [showPreviewTicketModal, setShowPreviewTicketModal] = useState(false);
|
||||
|
||||
// TODO: experiment to see if we need this state and effect or can we rely solely on the router/location for the dropdown state?
|
||||
useEffect(() => {
|
||||
setFilterVuln(!!location.query.vulnerable);
|
||||
setFilterVuln(location?.query?.vulnerable === "true" || false);
|
||||
// TODO: handle invalid values for vulnerable param
|
||||
}, [location]);
|
||||
|
||||
const { data: config } = useQuery(["config"], configAPI.loadAll, {
|
||||
onSuccess: (data) => {
|
||||
setIsSoftwareEnabled(data?.features?.enable_software_inventory);
|
||||
let jiraIntegrationEnabled = false;
|
||||
if (data.integrations.jira) {
|
||||
jiraIntegrationEnabled = data?.integrations.jira.some(
|
||||
(integration: IIntegration) => {
|
||||
return integration.enable_software_vulnerabilities;
|
||||
}
|
||||
);
|
||||
}
|
||||
let zendeskIntegrationEnabled = false;
|
||||
if (data.integrations.zendesk) {
|
||||
zendeskIntegrationEnabled = data?.integrations.zendesk.some(
|
||||
(integration: IIntegration) => {
|
||||
return integration.enable_software_vulnerabilities;
|
||||
}
|
||||
);
|
||||
}
|
||||
setIsVulnerabilityAutomationsEnabled(
|
||||
data?.webhook_settings?.vulnerabilities_webhook
|
||||
.enable_vulnerabilities_webhook ||
|
||||
jiraIntegrationEnabled ||
|
||||
zendeskIntegrationEnabled
|
||||
);
|
||||
// Convert from nanosecond to nearest day
|
||||
setRecentVulnerabilityMaxAge(
|
||||
Math.round(
|
||||
data?.vulnerabilities?.recent_vulnerability_max_age / 86400000000000
|
||||
)
|
||||
);
|
||||
// softwareConfig is either the global config or the team config of the currently selected team
|
||||
const {
|
||||
data: softwareConfig,
|
||||
error: softwareConfigError,
|
||||
isFetching: isFetchingSoftwareConfig,
|
||||
refetch: refetchSoftwareConfig,
|
||||
} = useQuery<
|
||||
IConfig | ILoadTeamResponse,
|
||||
Error,
|
||||
IConfig | ITeamConfig,
|
||||
ISoftwareConfigQueryKey[]
|
||||
>(
|
||||
[{ scope: "softwareConfig", teamId: currentTeam?.id }],
|
||||
({ queryKey }) => {
|
||||
const { teamId } = queryKey[0];
|
||||
return teamId ? teamsAPI.load(teamId) : configAPI.loadAll();
|
||||
},
|
||||
});
|
||||
{
|
||||
select: (data) => ("team" in data ? data.team : data),
|
||||
}
|
||||
);
|
||||
|
||||
const isSoftwareConfigLoaded =
|
||||
!isFetchingSoftwareConfig && !softwareConfigError && !!softwareConfig;
|
||||
|
||||
const isSoftwareEnabled = !!softwareConfig?.features
|
||||
?.enable_software_inventory;
|
||||
|
||||
const vulnWebhookSettings =
|
||||
softwareConfig?.webhook_settings?.vulnerabilities_webhook;
|
||||
|
||||
const isVulnWebhookEnabled = !!vulnWebhookSettings?.enable_vulnerabilities_webhook;
|
||||
|
||||
const isVulnIntegrationEnabled = (integrations?: IIntegrations) => {
|
||||
return (
|
||||
!!integrations?.jira?.some((j) => j.enable_software_vulnerabilities) ||
|
||||
!!integrations?.zendesk?.some((z) => z.enable_software_vulnerabilities)
|
||||
);
|
||||
};
|
||||
|
||||
const isAnyVulnAutomationEnabled =
|
||||
isVulnWebhookEnabled ||
|
||||
isVulnIntegrationEnabled(softwareConfig?.integrations);
|
||||
|
||||
const recentVulnerabilityMaxAge = (() => {
|
||||
let maxAgeInNanoseconds: number | undefined;
|
||||
if (softwareConfig && "vulnerabilities" in softwareConfig) {
|
||||
maxAgeInNanoseconds =
|
||||
softwareConfig.vulnerabilities.recent_vulnerability_max_age;
|
||||
} else {
|
||||
maxAgeInNanoseconds =
|
||||
globalConfig?.vulnerabilities.recent_vulnerability_max_age;
|
||||
}
|
||||
return maxAgeInNanoseconds
|
||||
? Math.round(maxAgeInNanoseconds / 86400000000000) // convert from nanoseconds to days
|
||||
: CONFIG_DEFAULT_RECENT_VULNERABILITY_MAX_AGE_IN_DAYS;
|
||||
})();
|
||||
|
||||
const {
|
||||
data: software,
|
||||
@ -191,8 +217,9 @@ const ManageSoftwarePage = ({
|
||||
({ queryKey }) => softwareAPI.load(queryKey[0]),
|
||||
{
|
||||
enabled:
|
||||
isOnGlobalTeam ||
|
||||
!!availableTeams?.find((t) => t.id === currentTeam?.id),
|
||||
isSoftwareConfigLoaded &&
|
||||
(isOnGlobalTeam ||
|
||||
!!availableTeams?.find((t) => t.id === currentTeam?.id)),
|
||||
keepPreviousData: true,
|
||||
staleTime: 30000, // stale time can be adjusted if fresher data is desired based on software inventory interval
|
||||
}
|
||||
@ -221,8 +248,9 @@ const ManageSoftwarePage = ({
|
||||
},
|
||||
{
|
||||
enabled:
|
||||
isOnGlobalTeam ||
|
||||
!!availableTeams?.find((t) => t.id === currentTeam?.id),
|
||||
isSoftwareConfigLoaded &&
|
||||
(isOnGlobalTeam ||
|
||||
!!availableTeams?.find((t) => t.id === currentTeam?.id)),
|
||||
keepPreviousData: true,
|
||||
staleTime: 30000, // stale time can be adjusted if fresher data is desired based on software inventory interval
|
||||
refetchOnWindowFocus: false,
|
||||
@ -231,21 +259,6 @@ const ManageSoftwarePage = ({
|
||||
}
|
||||
);
|
||||
|
||||
const canAddOrRemoveSoftwareWebhook = isGlobalAdmin || isGlobalMaintainer;
|
||||
|
||||
const {
|
||||
data: softwareVulnerabilitiesWebhook,
|
||||
isLoading: isLoadingSoftwareVulnerabilitiesWebhook,
|
||||
refetch: refetchSoftwareVulnerabilitiesWebhook,
|
||||
} = useQuery<IConfig, Error, IWebhookSoftwareVulnerabilities>(
|
||||
["config"],
|
||||
() => configAPI.loadAll(),
|
||||
{
|
||||
enabled: canAddOrRemoveSoftwareWebhook,
|
||||
select: (data: IConfig) => data.webhook_settings.vulnerabilities_webhook,
|
||||
}
|
||||
);
|
||||
|
||||
const onQueryChange = useDebouncedCallback(
|
||||
async ({
|
||||
pageIndex: newPageIndex,
|
||||
@ -270,8 +283,9 @@ const ManageSoftwarePage = ({
|
||||
300
|
||||
);
|
||||
|
||||
const toggleManageAutomationsModal = () =>
|
||||
const toggleManageAutomationsModal = useCallback(() => {
|
||||
setShowManageAutomationsModal(!showManageAutomationsModal);
|
||||
}, [setShowManageAutomationsModal, showManageAutomationsModal]);
|
||||
|
||||
const togglePreviewPayloadModal = useCallback(() => {
|
||||
setShowPreviewPayloadModal(!showPreviewPayloadModal);
|
||||
@ -281,10 +295,6 @@ const ManageSoftwarePage = ({
|
||||
setShowPreviewTicketModal(!showPreviewTicketModal);
|
||||
}, [setShowPreviewTicketModal, showPreviewTicketModal]);
|
||||
|
||||
const onManageAutomationsClick = () => {
|
||||
toggleManageAutomationsModal();
|
||||
};
|
||||
|
||||
const onCreateWebhookSubmit = async (
|
||||
configSoftwareAutomations: ISoftwareAutomations
|
||||
) => {
|
||||
@ -295,7 +305,7 @@ const ManageSoftwarePage = ({
|
||||
"success",
|
||||
"Successfully updated vulnerability automations."
|
||||
);
|
||||
refetchSoftwareVulnerabilitiesWebhook();
|
||||
refetchSoftwareConfig();
|
||||
});
|
||||
} catch {
|
||||
renderFlash(
|
||||
@ -311,28 +321,34 @@ const ManageSoftwarePage = ({
|
||||
setPageIndex(0);
|
||||
};
|
||||
|
||||
const renderHeaderButtons = (
|
||||
state: IHeaderButtonsState
|
||||
): JSX.Element | null => {
|
||||
if (
|
||||
!softwareError &&
|
||||
state.isGlobalAdmin &&
|
||||
(!state.isPremiumTier || state.teamId === 0) &&
|
||||
!state.isLoading
|
||||
) {
|
||||
return (
|
||||
<Button
|
||||
onClick={onManageAutomationsClick}
|
||||
className={`${baseClass}__manage-automations button`}
|
||||
variant="brand"
|
||||
>
|
||||
<span>Manage automations</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// TODO: refactor/replace team dropdown header component in accordance with new patterns
|
||||
const renderHeaderButtons = useCallback(
|
||||
(state: IHeaderButtonsState): JSX.Element | null => {
|
||||
const {
|
||||
teamId,
|
||||
isLoading,
|
||||
isGlobalAdmin,
|
||||
isPremiumTier: isPremium,
|
||||
} = state;
|
||||
const canManageAutomations =
|
||||
isGlobalAdmin && (!isPremium || teamId === 0);
|
||||
if (canManageAutomations && !softwareError && !isLoading) {
|
||||
return (
|
||||
<Button
|
||||
onClick={toggleManageAutomationsModal}
|
||||
className={`${baseClass}__manage-automations button`}
|
||||
variant="brand"
|
||||
>
|
||||
<span>Manage automations</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[softwareError, toggleManageAutomationsModal]
|
||||
);
|
||||
|
||||
// TODO: refactor/replace team dropdown header component in accordance with new patterns
|
||||
const renderHeaderDescription = (state: ITeamsDropdownState) => {
|
||||
return (
|
||||
<p>
|
||||
@ -351,6 +367,7 @@ const ManageSoftwarePage = ({
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: refactor/replace team dropdown header component in accordance with new patterns
|
||||
const renderHeader = useCallback(() => {
|
||||
return (
|
||||
<TeamsDropdownHeader
|
||||
@ -363,12 +380,12 @@ const ManageSoftwarePage = ({
|
||||
buttons={(state) =>
|
||||
renderHeaderButtons({
|
||||
...state,
|
||||
isLoading: isLoadingSoftwareVulnerabilitiesWebhook,
|
||||
isLoading: !isSoftwareConfigLoaded,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}, [router, location, isLoadingSoftwareVulnerabilitiesWebhook]);
|
||||
}, [router, location, isSoftwareConfigLoaded, renderHeaderButtons]);
|
||||
|
||||
const renderSoftwareCount = useCallback(() => {
|
||||
const count = softwareCount;
|
||||
@ -386,7 +403,6 @@ const ManageSoftwarePage = ({
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Use setInterval to keep last updated time current?
|
||||
if (count) {
|
||||
return (
|
||||
<div
|
||||
@ -412,7 +428,7 @@ const ManageSoftwarePage = ({
|
||||
isSoftwareEnabled,
|
||||
]);
|
||||
|
||||
// TODO: retool this with react-router location descriptor objects
|
||||
// TODO: refactor in accordance with new patterns for query params and management of URL state
|
||||
const buildUrlQueryString = (queryString: string, vulnerable: boolean) => {
|
||||
queryString = queryString.startsWith("?")
|
||||
? queryString.slice(1)
|
||||
@ -438,6 +454,7 @@ const ManageSoftwarePage = ({
|
||||
return queryString;
|
||||
};
|
||||
|
||||
// TODO: refactor in accordance with new patterns for query params and management of URL state
|
||||
const onVulnFilterChange = useCallback(
|
||||
(vulnerable: boolean) => {
|
||||
setFilterVuln(vulnerable);
|
||||
@ -499,14 +516,17 @@ const ManageSoftwarePage = ({
|
||||
[isPremiumTier]
|
||||
);
|
||||
|
||||
return !availableTeams || !config ? (
|
||||
return !availableTeams ||
|
||||
!globalConfig ||
|
||||
(!softwareConfig && !softwareConfigError) ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<MainContent>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
{renderHeader()}
|
||||
<div className={`${baseClass}__table`}>
|
||||
{softwareError && !isFetchingSoftware ? (
|
||||
{(softwareError && !isFetchingSoftware) ||
|
||||
(softwareConfigError && !isFetchingSoftwareConfig) ? (
|
||||
<TableDataError />
|
||||
) : (
|
||||
<TableContainer
|
||||
@ -552,18 +572,9 @@ const ManageSoftwarePage = ({
|
||||
togglePreviewTicketModal={togglePreviewTicketModal}
|
||||
showPreviewPayloadModal={showPreviewPayloadModal}
|
||||
showPreviewTicketModal={showPreviewTicketModal}
|
||||
softwareVulnerabilityAutomationEnabled={
|
||||
isVulnerabilityAutomationsEnabled
|
||||
}
|
||||
softwareVulnerabilityWebhookEnabled={
|
||||
softwareVulnerabilitiesWebhook &&
|
||||
softwareVulnerabilitiesWebhook.enable_vulnerabilities_webhook
|
||||
}
|
||||
currentDestinationUrl={
|
||||
(softwareVulnerabilitiesWebhook &&
|
||||
softwareVulnerabilitiesWebhook.destination_url) ||
|
||||
""
|
||||
}
|
||||
softwareVulnerabilityAutomationEnabled={isAnyVulnAutomationEnabled}
|
||||
softwareVulnerabilityWebhookEnabled={isVulnWebhookEnabled}
|
||||
currentDestinationUrl={vulnWebhookSettings?.destination_url || ""}
|
||||
recentVulnerabilityMaxAge={recentVulnerabilityMaxAge}
|
||||
/>
|
||||
)}
|
||||
|
@ -11,7 +11,10 @@ import {
|
||||
IIntegrations,
|
||||
IIntegrationType,
|
||||
} from "interfaces/integration";
|
||||
import { IConfig } from "interfaces/config";
|
||||
import {
|
||||
IConfig,
|
||||
CONFIG_DEFAULT_RECENT_VULNERABILITY_MAX_AGE_IN_DAYS,
|
||||
} from "interfaces/config";
|
||||
import configAPI from "services/entities/config";
|
||||
|
||||
import ReactTooltip from "react-tooltip";
|
||||
@ -26,7 +29,7 @@ import InputField from "components/forms/fields/InputField";
|
||||
|
||||
import { IWebhookSoftwareVulnerabilities } from "interfaces/webhook";
|
||||
import useDeepEffect from "hooks/useDeepEffect";
|
||||
import _, { size } from "lodash";
|
||||
import { size } from "lodash";
|
||||
|
||||
import PreviewPayloadModal from "../PreviewPayloadModal";
|
||||
import PreviewTicketModal from "../PreviewTicketModal";
|
||||
@ -327,7 +330,9 @@ const ManageAutomationsModal = ({
|
||||
<p>
|
||||
A ticket will be created in your <b>Integration</b> if a detected
|
||||
vulnerability (CVE) was published in the last{" "}
|
||||
{recentVulnerabilityMaxAge || "30"} days.
|
||||
{recentVulnerabilityMaxAge ||
|
||||
CONFIG_DEFAULT_RECENT_VULNERABILITY_MAX_AGE_IN_DAYS}{" "}
|
||||
days.
|
||||
</p>
|
||||
</div>
|
||||
{(jiraIntegrationsIndexed && jiraIntegrationsIndexed.length > 0) ||
|
||||
|
@ -5,10 +5,12 @@ import { pick } from "lodash";
|
||||
|
||||
import { buildQueryStringFromParams } from "utilities/url";
|
||||
import { IEnrollSecret } from "interfaces/enroll_secret";
|
||||
import { IIntegrations } from "interfaces/integration";
|
||||
import {
|
||||
INewMembersBody,
|
||||
IRemoveMembersBody,
|
||||
ITeamConfig,
|
||||
ITeamWebhookSettings,
|
||||
} from "interfaces/team";
|
||||
|
||||
interface ILoadTeamsParams {
|
||||
@ -33,6 +35,12 @@ export interface ITeamFormData {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IUpdateTeamFormData {
|
||||
name: string;
|
||||
webhook_settings: Partial<ITeamWebhookSettings>;
|
||||
integrations: IIntegrations;
|
||||
}
|
||||
|
||||
export default {
|
||||
create: (formData: ITeamFormData) => {
|
||||
const { TEAMS } = endpoints;
|
||||
@ -65,7 +73,7 @@ export default {
|
||||
return sendRequest("GET", path);
|
||||
},
|
||||
update: (
|
||||
{ name, webhook_settings, integrations }: Partial<ITeamConfig>,
|
||||
{ name, webhook_settings, integrations }: Partial<IUpdateTeamFormData>,
|
||||
teamId?: number
|
||||
): Promise<ITeamConfig> => {
|
||||
if (typeof teamId === "undefined") {
|
||||
|
Loading…
Reference in New Issue
Block a user