From 0a423f4c7b17a60e3fc337a07daef9c4b4410cba Mon Sep 17 00:00:00 2001 From: gillespi314 <73313222+gillespi314@users.noreply.github.com> Date: Fri, 1 Oct 2021 19:35:13 -0500 Subject: [PATCH] Add new feature: Team policies (#2142) --- .../side_panels/SiteTopNav/navItems.js | 29 +-- .../hosts/ManageHostsPage/ManageHostsPage.tsx | 9 +- .../ManagePoliciesPage/ManagePoliciesPage.tsx | 207 ++++++++++-------- .../policies/ManagePoliciesPage/_styles.scss | 9 +- .../PoliciesListWrapper.tsx | 6 +- frontend/router/index.tsx | 5 - 6 files changed, 128 insertions(+), 137 deletions(-) diff --git a/frontend/components/side_panels/SiteTopNav/navItems.js b/frontend/components/side_panels/SiteTopNav/navItems.js index bce498f1e1..64100e633e 100644 --- a/frontend/components/side_panels/SiteTopNav/navItems.js +++ b/frontend/components/side_panels/SiteTopNav/navItems.js @@ -69,37 +69,20 @@ export default (currentUser) => { }, ]; - const globalMaintainerNavItems = [ - { - icon: "policies", - name: "Policies", - iconName: "policies", - location: { - regex: new RegExp(`^${URL_PREFIX}/(policies)/`), - pathname: PATHS.MANAGE_POLICIES, - }, - }, - ]; - if (permissionUtils.isGlobalAdmin(currentUser)) { return [ ...userNavItems, ...teamMaintainerNavItems, - ...globalMaintainerNavItems, + ...policiesTab, ...adminNavItems, ]; } - if (permissionUtils.isGlobalMaintainer(currentUser)) { - return [ - ...userNavItems, - ...teamMaintainerNavItems, - ...globalMaintainerNavItems, - ]; - } - - if (permissionUtils.isAnyTeamMaintainer(currentUser)) { - return [...userNavItems, ...teamMaintainerNavItems]; + if ( + permissionUtils.isGlobalMaintainer(currentUser) || + permissionUtils.isAnyTeamMaintainer(currentUser) + ) { + return [...userNavItems, ...teamMaintainerNavItems, ...policiesTab]; } return [...userNavItems, ...policiesTab]; diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx index f249ca4755..d5d6a65113 100644 --- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx +++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx @@ -80,6 +80,9 @@ interface IManageHostsProps { interface ILabelsResponse { labels: ILabel[]; } +interface IPolicyResponse { + policy: IPolicy; +} interface ITeamsResponse { teams: ITeam[]; @@ -222,7 +225,7 @@ const ManageHostsPage = ({ } ); - useQuery( + useQuery( ["policy"], () => { const request = currentTeam @@ -232,8 +235,8 @@ const ManageHostsPage = ({ }, { enabled: !!policyId, - onSuccess: ({ query_name }) => { - setPolicyName(query_name); + onSuccess: ({ policy }) => { + setPolicyName(policy.query_name); }, } ); diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx index f9e7cf31c6..b3d2d79a65 100644 --- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx @@ -37,13 +37,16 @@ const baseClass = "manage-policies-page"; const DOCS_LINK = "https://fleetdm.com/docs/deploying/configuration#osquery_detail_update_interval"; -const INHERITED_POLICIES_COUNT_HTML = ( - - {" "} - inherited from{" "} - All teams policy - -); +const renderInheritedPoliciesButtonText = ( + showPolicies: boolean, + policies: IPolicy[] +) => { + const count = policies.length; + + return `${showPolicies ? "Hide" : "Show"} ${count} inherited ${ + count > 1 ? "policies" : "policy" + }`; +}; const ManagePolicyPage = (managePoliciesPageProps: { router: any; @@ -150,16 +153,15 @@ const ManagePolicyPage = (managePoliciesPageProps: { [getGlobalPolicies, getTeamPolicies] ); - const handleChangeSelectedTeam = useCallback( - (id: number) => { - const { MANAGE_POLICIES } = PATHS; - const path = id ? `${MANAGE_POLICIES}?team_id=${id}` : MANAGE_POLICIES; - router.replace(path); - setShowInheritedPolicies(false); - setSelectedPolicyIds([]); - }, - [router] - ); + const handleChangeSelectedTeam = (id: number) => { + const { MANAGE_POLICIES } = PATHS; + const path = id ? `${MANAGE_POLICIES}?team_id=${id}` : MANAGE_POLICIES; + router.replace(path); + setShowInheritedPolicies(false); + setSelectedPolicyIds([]); + setGlobalPolicies([]); + setTeamPolicies([]); + }; const toggleAddPolicyModal = () => setShowAddPolicyModal(!showAddPolicyModal); @@ -240,8 +242,8 @@ const ManagePolicyPage = (managePoliciesPageProps: { unsortedTeams = currentUser.teams; } if (unsortedTeams !== null) { - const sortedTeams = unsortedTeams.sort( - (a, b) => sortUtils.caseInsensitiveAsc(a.name, b.name) // TODO: confirm that sortUtils refactor in #2105 has been merged first + const sortedTeams = unsortedTeams.sort((a, b) => + sortUtils.caseInsensitiveAsc(a.name, b.name) ); setUserTeams(sortedTeams); } @@ -280,7 +282,7 @@ const ManagePolicyPage = (managePoliciesPageProps: { // so a falsiness check cannot be used here. Null case here allows us to skip API call // that would be triggered on a change to selectedTeamId. teamId !== null && setSelectedTeamId(teamId); - }, [handleChangeSelectedTeam, isOnGlobalTeam, location, userTeams]); + }, [isOnGlobalTeam, location, userTeams]); // Watch for selected team changes and call getPolicies to make new policies API request. useEffect(() => { @@ -294,7 +296,13 @@ const ManagePolicyPage = (managePoliciesPageProps: { getTeamPolicies(selectedTeamId); } } - }, [getGlobalPolicies, getTeamPolicies, isOnGlobalTeam, selectedTeamId]); + }, [ + getGlobalPolicies, + getTeamPolicies, + isAnyTeamMaintainer, + isOnGlobalTeam, + selectedTeamId, + ]); // Pull osquery detail update interval value from config, reformat, and set as updateInterval. useEffect(() => { @@ -305,6 +313,28 @@ const ManagePolicyPage = (managePoliciesPageProps: { } }, [config]); + // If the user is free tier or if there is no selected team, we show the default description. + // We also want to check selectTeamId for the null case so that we don't render the element prematurely. + const showDefaultDescription = + isFreeTier || (isPremiumTier && !selectedTeamId && selectedTeamId !== null); + + // If there aren't any policies of if there are loading errors, we don't show the update interval info banner. + // We also want to check selectTeamId for the null case so that we don't render the element prematurely. + const showInfoBanner = + (selectedTeamId && !isTeamPoliciesError && !!teamPolicies?.length) || + (!selectedTeamId && + selectedTeamId !== null && + !isGlobalPoliciesError && + !!globalPolicies?.length); + + // If there aren't any policies of if there are loading errors, we don't show the inherited policies button. + const showInheritedPoliciesButton = + !!selectedTeamId && + !!teamPolicies?.length && + !!globalPolicies?.length && + !isGlobalPoliciesError && + !isTeamPoliciesError; + return (
@@ -344,35 +374,29 @@ const ManagePolicyPage = (managePoliciesPageProps: { .

)} - {isFreeTier || - (isPremiumTier && !selectedTeamId && selectedTeamId !== null && ( -

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

- ))} -
- {updateInterval && - ((selectedTeamId && !isTeamPoliciesError && !!teamPolicies?.length) || - (!selectedTeamId && - selectedTeamId !== null && - !isGlobalPoliciesError && - !!globalPolicies?.length)) && ( - -

- Your policies are checked every {updateInterval.trim()}.{" "} - {isGlobalAdmin && ( - - Check out the Fleet documentation on{" "} - - how to edit this frequency - - . - - )} -

-
+ {showDefaultDescription && ( +

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

)} +
+ {!!updateInterval && showInfoBanner && ( + +

+ Your policies are checked every {updateInterval.trim()}.{" "} + {isGlobalAdmin && ( + + Check out the Fleet documentation on{" "} + + how to edit this frequency + + . + + )} +

+
+ )}
{!!selectedTeamId && (isTeamPoliciesError ? ( @@ -407,59 +431,48 @@ const ManagePolicyPage = (managePoliciesPageProps: { /> ))}
- {!!selectedTeamId && - !!teamPolicies?.length && - !!globalPolicies?.length && - !isGlobalPoliciesError && - !isTeamPoliciesError && ( - - -
- + {renderInheritedPoliciesButtonText( + showInheritedPolicies, + globalPolicies + )} + +
+

“All teams” policies are checked
for this team’s hosts.

\ " - } - /> -
- - )} - {!!selectedTeamId && - !!teamPolicies?.length && - !!globalPolicies?.length && - !isGlobalPoliciesError && - !isTeamPoliciesError && - showInheritedPolicies && ( -
-
- )} + + )} + {showInheritedPoliciesButton && showInheritedPolicies && ( +
+ +
+ )} {showAddPolicyModal && ( void; toggleAddPolicyModal: () => void; resultsTitle?: string; - resultsHtml?: JSX.Element; - selectedTeamId?: number | undefined | null; + selectedTeamId?: number | null; canAddOrRemovePolicy?: boolean; tableType?: string; } @@ -28,7 +27,6 @@ const PoliciesListWrapper = (props: IPoliciesListWrapperProps): JSX.Element => { onRemovePoliciesClick, toggleAddPolicyModal, resultsTitle, - resultsHtml, selectedTeamId, canAddOrRemovePolicy, tableType, @@ -69,7 +67,6 @@ const PoliciesListWrapper = (props: IPoliciesListWrapperProps): JSX.Element => { > { primarySelectActionButtonText={"Remove"} emptyComponent={NoPolicies} onQueryChange={noop} + disableCount={tableType === "inheritedPolicies"} />
); diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx index 98ce4d8b5a..c83d205bbf 100644 --- a/frontend/router/index.tsx +++ b/frontend/router/index.tsx @@ -126,11 +126,6 @@ const routes = ( - - - - -