import React, { useCallback, useContext, useEffect, useState } from "react"; import { useQuery } from "react-query"; import { InjectedRouter } from "react-router/lib/Router"; import { get, has, noop } from "lodash"; import { AppContext } from "context/app"; import { PolicyContext } from "context/policy"; import { TableContext } from "context/table"; import { NotificationContext } from "context/notification"; import { IPolicyStats, ILoadAllPoliciesResponse } from "interfaces/policy"; import { IWebhookFailingPolicies } from "interfaces/webhook"; import { IConfig } from "interfaces/config"; import { ITeam, ILoadTeamResponse } from "interfaces/team"; import PATHS from "router/paths"; import configAPI from "services/entities/config"; import globalPoliciesAPI from "services/entities/global_policies"; import teamPoliciesAPI from "services/entities/team_policies"; import teamsAPI from "services/entities/teams"; import usersAPI, { IGetMeResponse } from "services/entities/users"; import Button from "components/buttons/Button"; import RevealButton from "components/buttons/RevealButton"; import Spinner from "components/Spinner"; import TeamsDropdown from "components/TeamsDropdown"; import TableDataError from "components/TableDataError"; import PoliciesListWrapper from "./components/PoliciesListWrapper"; import ManageAutomationsModal from "./components/ManageAutomationsModal"; import AddPolicyModal from "./components/AddPolicyModal"; import RemovePoliciesModal from "./components/RemovePoliciesModal"; interface IManagePoliciesPageProps { router: InjectedRouter; // v3 location: { action: string; hash: string; key: string; pathname: string; query: { team_id?: string }; search: string; }; } const baseClass = "manage-policies-page"; const ManagePolicyPage = ({ router, location, }: IManagePoliciesPageProps): JSX.Element => { const { availableTeams, isGlobalAdmin, isGlobalMaintainer, isOnGlobalTeam, isFreeTier, isPremiumTier, isTeamAdmin, isTeamMaintainer, currentTeam, setAvailableTeams, setCurrentUser, setCurrentTeam, setConfig, } = useContext(AppContext); const { renderFlash } = useContext(NotificationContext); const teamId = location?.query?.team_id ? parseInt(location?.query?.team_id, 10) : 0; const { setLastEditedQueryName, setLastEditedQueryDescription, setLastEditedQueryResolution, setLastEditedQueryPlatform, } = useContext(PolicyContext); const { setResetSelectedRows } = useContext(TableContext); const [selectedPolicyIds, setSelectedPolicyIds] = useState([]); const [showManageAutomationsModal, setShowManageAutomationsModal] = useState( false ); const [showPreviewPayloadModal, setShowPreviewPayloadModal] = useState(false); const [showAddPolicyModal, setShowAddPolicyModal] = useState(false); const [showRemovePoliciesModal, setShowRemovePoliciesModal] = useState(false); const [showInheritedPolicies, setShowInheritedPolicies] = useState(false); const [ failingPoliciesWebhook, setFailingPoliciesWebhook, ] = useState(); const [currentAutomatedPolicies, setCurrentAutomatedPolicies] = useState< number[] >(); useEffect(() => { setLastEditedQueryPlatform(null); }, []); useQuery(["me"], () => usersAPI.me(), { onSuccess: ({ user, available_teams }: IGetMeResponse) => { setCurrentUser(user); setAvailableTeams(available_teams); }, }); const { data: globalPolicies, error: globalPoliciesError, isLoading: isLoadingGlobalPolicies, isStale: isStaleGlobalPolicies, refetch: refetchGlobalPolicies, } = useQuery( ["globalPolicies"], () => { return globalPoliciesAPI.loadAll(); }, { enabled: !!availableTeams, select: (data) => data.policies, staleTime: 3000, } ); const { data: teamPolicies, error: teamPoliciesError, isLoading: isLoadingTeamPolicies, refetch: refetchTeamPolicies, } = useQuery( ["teamPolicies", teamId], () => teamPoliciesAPI.loadAll(teamId), { enabled: !!availableTeams && isPremiumTier && !!teamId, select: (data) => data.policies, } ); const canAddOrRemovePolicy = isGlobalAdmin || isGlobalMaintainer || isTeamMaintainer || isTeamAdmin; const canManageAutomations = isGlobalAdmin || isTeamAdmin; const { isLoading: isLoadingWebhooks, refetch: refetchWebhooks } = useQuery< IConfig | ILoadTeamResponse, Error, IConfig | ITeam >( ["webhooks", teamId], () => { return teamId ? teamsAPI.load(teamId) : configAPI.loadAll(); }, { enabled: canAddOrRemovePolicy, select: (data) => { if (has(data, "team")) { return get(data, "team"); } return data; }, onSuccess: (data) => { setFailingPoliciesWebhook( data.webhook_settings?.failing_policies_webhook ); setCurrentAutomatedPolicies( data.webhook_settings?.failing_policies_webhook.policy_ids ); if (has(data, "org_info")) { setConfig(data as IConfig); } }, } ); const refetchPolicies = (id?: number) => { refetchGlobalPolicies(); if (id) { refetchTeamPolicies(); } }; const findAvailableTeam = (id: number) => { return availableTeams?.find((t) => t.id === id); }; const handleTeamSelect = (id: number) => { const { MANAGE_POLICIES } = PATHS; const selectedTeam = findAvailableTeam(id); const path = selectedTeam?.id ? `${MANAGE_POLICIES}?team_id=${selectedTeam.id}` : MANAGE_POLICIES; router.replace(path); setShowInheritedPolicies(false); setSelectedPolicyIds([]); setCurrentTeam(selectedTeam); isStaleGlobalPolicies && refetchGlobalPolicies(); }; const toggleManageAutomationsModal = () => setShowManageAutomationsModal(!showManageAutomationsModal); const togglePreviewPayloadModal = useCallback(() => { setShowPreviewPayloadModal(!showPreviewPayloadModal); }, [setShowPreviewPayloadModal, showPreviewPayloadModal]); const toggleAddPolicyModal = () => setShowAddPolicyModal(!showAddPolicyModal); const toggleRemovePoliciesModal = () => setShowRemovePoliciesModal(!showRemovePoliciesModal); const toggleShowInheritedPolicies = () => setShowInheritedPolicies(!showInheritedPolicies); const onCreateWebhookSubmit = async ({ destination_url, policy_ids, enable_failing_policies_webhook, }: IWebhookFailingPolicies) => { try { const api = teamId ? teamsAPI : configAPI; const secondParam = teamId || undefined; const data = { webhook_settings: { failing_policies_webhook: { destination_url, policy_ids, enable_failing_policies_webhook, }, }, }; const request = api.update(data, secondParam); await request.then(() => { renderFlash("success", "Successfully updated policy automations."); }); } catch { renderFlash( "error", "Could not update policy automations. Please try again." ); } finally { toggleManageAutomationsModal(); refetchWebhooks(); } }; const onAddPolicyClick = () => { setLastEditedQueryName(""); setLastEditedQueryDescription(""); setLastEditedQueryResolution(""); toggleAddPolicyModal(); }; const onRemovePoliciesClick = (selectedTableIds: number[]): void => { toggleRemovePoliciesModal(); setSelectedPolicyIds(selectedTableIds); }; const onRemovePoliciesSubmit = async () => { const id = currentTeam?.id; try { const request = id ? teamPoliciesAPI.destroy(id, selectedPolicyIds) : globalPoliciesAPI.destroy(selectedPolicyIds); await request.then(() => { renderFlash( "success", `Successfully removed ${ selectedPolicyIds?.length === 1 ? "policy" : "policies" }.` ); setResetSelectedRows(true); refetchPolicies(id); }); } catch { renderFlash( "error", `Unable to remove ${ selectedPolicyIds?.length === 1 ? "policy" : "policies" }. Please try again.` ); } finally { toggleRemovePoliciesModal(); } }; const inheritedPoliciesButtonText = ( showPolicies: boolean, count: number ) => { return `${showPolicies ? "Hide" : "Show"} ${count} inherited ${ count > 1 ? "policies" : "policy" }`; }; const showTeamDescription = isPremiumTier && !!teamId; const showInheritedPoliciesButton = !!teamId && !isLoadingTeamPolicies && !teamPoliciesError && !isLoadingGlobalPolicies && !globalPoliciesError && !!globalPolicies?.length; const availablePoliciesForAutomation = (teamId ? teamPolicies : globalPolicies) || []; // If team_id from URL query params is not valid, we instead use a default team // either the current team (if any) or all teams (for global users) or // the first available team (for non-global users) const getValidatedTeamId = () => { if (findAvailableTeam(teamId)) { return teamId; } if (!teamId && currentTeam) { return currentTeam.id; } if (!teamId && !currentTeam && !isOnGlobalTeam && availableTeams) { return availableTeams[0]?.id; } return 0; }; // If team_id or currentTeam doesn't match validated id, switch to validated id useEffect(() => { if (availableTeams) { const validatedId = getValidatedTeamId(); if (validatedId !== currentTeam?.id || validatedId !== teamId) { handleTeamSelect(validatedId); } } }, [availableTeams]); return !availableTeams ? ( ) : (
{isFreeTier &&

Policies

} {isPremiumTier && (availableTeams.length > 1 || isOnGlobalTeam) && ( handleTeamSelect(newSelectedValue) } /> )} {isPremiumTier && !isOnGlobalTeam && availableTeams.length === 1 && (

{availableTeams[0].name}

)}
{canManageAutomations && !isLoadingWebhooks && !isLoadingGlobalPolicies && ( )} {canAddOrRemovePolicy && (
)}
{showTeamDescription ? (

Add additional policies for all hosts assigned to this team .

) : (

Add policies for all of your hosts to see which pass your organization’s standards.

)}
{!!teamId && teamPoliciesError && } {!!teamId && !teamPoliciesError && (isLoadingTeamPolicies && isLoadingWebhooks ? ( ) : ( ))} {!teamId && globalPoliciesError && } {!teamId && !globalPoliciesError && (isLoadingGlobalPolicies ? ( ) : ( ))}
{showInheritedPoliciesButton && globalPolicies && ( for this team’s hosts.' } onClick={toggleShowInheritedPolicies} /> )} {showInheritedPoliciesButton && showInheritedPolicies && (
{globalPoliciesError && } {!globalPoliciesError && (isLoadingGlobalPolicies ? ( ) : ( ))}
)} {showManageAutomationsModal && ( )} {showAddPolicyModal && ( )} {showRemovePoliciesModal && ( )}
); }; export default ManagePolicyPage;