2021-09-30 19:32:06 +00:00
|
|
|
|
import React, { useCallback, useContext, useEffect, useState } from "react";
|
2021-11-24 17:16:42 +00:00
|
|
|
|
import { Link } from "react-router";
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import { useQuery } from "react-query";
|
|
|
|
|
import { useDispatch } from "react-redux";
|
|
|
|
|
import { noop } from "lodash";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import { renderFlash } from "redux/nodes/notifications/actions";
|
|
|
|
|
|
|
|
|
|
import PATHS from "router/paths";
|
|
|
|
|
|
2021-08-30 23:02:53 +00:00
|
|
|
|
import { IPolicy } from "interfaces/policy";
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import { ITeam } from "interfaces/team";
|
|
|
|
|
import { IUser } from "interfaces/user";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import { AppContext } from "context/app";
|
|
|
|
|
|
|
|
|
|
import fleetQueriesAPI from "services/entities/queries";
|
|
|
|
|
import globalPoliciesAPI from "services/entities/global_policies";
|
|
|
|
|
import teamsAPI from "services/entities/teams";
|
|
|
|
|
import teamPoliciesAPI from "services/entities/team_policies";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
|
|
|
|
import { inMilliseconds, secondsToHms } from "fleet/helpers";
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import sortUtils from "utilities/sort";
|
|
|
|
|
import permissionsUtils from "utilities/permissions";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-02 12:16:19 +00:00
|
|
|
|
import TableDataError from "components/TableDataError";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
import Button from "components/buttons/Button";
|
|
|
|
|
import InfoBanner from "components/InfoBanner/InfoBanner";
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import IconToolTip from "components/IconToolTip";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
import PoliciesListWrapper from "./components/PoliciesListWrapper";
|
|
|
|
|
import RemovePoliciesModal from "./components/RemovePoliciesModal";
|
2021-09-30 19:32:06 +00:00
|
|
|
|
import TeamsDropdown from "./components/TeamsDropdown";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
|
|
|
|
const baseClass = "manage-policies-page";
|
|
|
|
|
|
|
|
|
|
const DOCS_LINK =
|
2021-10-11 15:13:10 +00:00
|
|
|
|
"https://fleetdm.com/docs/deploying/configuration#osquery-policy-update-interval";
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-10-02 00:35:13 +00:00
|
|
|
|
const renderInheritedPoliciesButtonText = (
|
|
|
|
|
showPolicies: boolean,
|
|
|
|
|
policies: IPolicy[]
|
|
|
|
|
) => {
|
|
|
|
|
const count = policies.length;
|
|
|
|
|
|
|
|
|
|
return `${showPolicies ? "Hide" : "Show"} ${count} inherited ${
|
|
|
|
|
count > 1 ? "policies" : "policy"
|
|
|
|
|
}`;
|
|
|
|
|
};
|
2021-09-30 19:32:06 +00:00
|
|
|
|
|
|
|
|
|
const ManagePolicyPage = (managePoliciesPageProps: {
|
|
|
|
|
router: any;
|
|
|
|
|
location: any;
|
|
|
|
|
}): JSX.Element => {
|
|
|
|
|
const { location, router } = managePoliciesPageProps;
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
|
|
|
|
const dispatch = useDispatch();
|
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const {
|
|
|
|
|
config,
|
|
|
|
|
currentUser,
|
2021-10-26 20:55:53 +00:00
|
|
|
|
isAnyTeamMaintainerOrTeamAdmin,
|
2021-09-30 19:32:06 +00:00
|
|
|
|
isGlobalAdmin,
|
|
|
|
|
isGlobalMaintainer,
|
|
|
|
|
isOnGlobalTeam,
|
|
|
|
|
isFreeTier,
|
|
|
|
|
isPremiumTier,
|
|
|
|
|
} = useContext(AppContext);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-10-28 17:14:01 +00:00
|
|
|
|
const { isTeamMaintainer, isTeamAdmin } = permissionsUtils;
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const canAddOrRemovePolicy = (user: IUser | null, teamId: number | null) =>
|
2021-10-28 17:14:01 +00:00
|
|
|
|
isGlobalAdmin ||
|
|
|
|
|
isGlobalMaintainer ||
|
|
|
|
|
isTeamMaintainer(user, teamId) ||
|
|
|
|
|
isTeamAdmin(user, teamId);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
|
|
|
|
|
const { data: teams } = useQuery(["teams"], () => teamsAPI.loadAll({}), {
|
|
|
|
|
enabled: !!isPremiumTier,
|
|
|
|
|
select: (data) => data.teams,
|
|
|
|
|
refetchOnMount: false,
|
|
|
|
|
refetchOnWindowFocus: false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { data: fleetQueries } = useQuery(
|
|
|
|
|
["fleetQueries"],
|
|
|
|
|
() => fleetQueriesAPI.loadAll(),
|
|
|
|
|
{
|
|
|
|
|
select: (data) => data.queries,
|
|
|
|
|
refetchOnMount: false,
|
|
|
|
|
refetchOnWindowFocus: false,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// ===== local state
|
|
|
|
|
const [globalPolicies, setGlobalPolicies] = useState<IPolicy[] | never[]>([]);
|
|
|
|
|
const [isLoadingGlobalPolicies, setIsLoadingGlobalPolicies] = useState(true);
|
|
|
|
|
const [isGlobalPoliciesError, setIsGlobalPoliciesError] = useState(false);
|
|
|
|
|
const [teamPolicies, setTeamPolicies] = useState<IPolicy[] | never[]>([]);
|
|
|
|
|
const [isLoadingTeamPolicies, setIsLoadingTeamPolicies] = useState(true);
|
|
|
|
|
const [isTeamPoliciesError, setIsTeamPoliciesError] = useState(false);
|
|
|
|
|
const [userTeams, setUserTeams] = useState<ITeam[] | never[] | null>(null);
|
|
|
|
|
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(
|
|
|
|
|
parseInt(location?.query?.team_id, 10) || null
|
|
|
|
|
);
|
|
|
|
|
const [selectedPolicyIds, setSelectedPolicyIds] = useState<
|
|
|
|
|
number[] | never[]
|
|
|
|
|
>([]);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
const [showRemovePoliciesModal, setShowRemovePoliciesModal] = useState(false);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const [showInheritedPolicies, setShowInheritedPolicies] = useState(false);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
const [updateInterval, setUpdateInterval] = useState<string>(
|
2021-10-11 15:13:10 +00:00
|
|
|
|
"osquery policy update interval"
|
2021-08-30 23:02:53 +00:00
|
|
|
|
);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
// ===== local state
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const getGlobalPolicies = useCallback(async () => {
|
|
|
|
|
setIsLoadingGlobalPolicies(true);
|
|
|
|
|
setIsGlobalPoliciesError(false);
|
|
|
|
|
let result;
|
2021-08-30 23:02:53 +00:00
|
|
|
|
try {
|
2021-09-30 19:32:06 +00:00
|
|
|
|
result = await globalPoliciesAPI
|
|
|
|
|
.loadAll()
|
|
|
|
|
.then((response) => response.policies);
|
|
|
|
|
setGlobalPolicies(result);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
setIsGlobalPoliciesError(true);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
} finally {
|
2021-09-30 19:32:06 +00:00
|
|
|
|
setIsLoadingGlobalPolicies(false);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
return result;
|
|
|
|
|
}, []);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const getTeamPolicies = useCallback(async (teamId) => {
|
|
|
|
|
setIsLoadingTeamPolicies(true);
|
|
|
|
|
setIsTeamPoliciesError(false);
|
|
|
|
|
let result;
|
2021-08-30 23:02:53 +00:00
|
|
|
|
try {
|
2021-09-30 19:32:06 +00:00
|
|
|
|
result = await teamPoliciesAPI
|
|
|
|
|
.loadAll(teamId)
|
|
|
|
|
.then((response) => response.policies);
|
|
|
|
|
setTeamPolicies(result);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
setIsTeamPoliciesError(true);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoadingTeamPolicies(false);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
return result;
|
|
|
|
|
}, []);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const getPolicies = useCallback(
|
|
|
|
|
(teamId) => {
|
|
|
|
|
return teamId ? getTeamPolicies(teamId) : getGlobalPolicies();
|
|
|
|
|
},
|
|
|
|
|
[getGlobalPolicies, getTeamPolicies]
|
|
|
|
|
);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-10-02 00:35:13 +00:00
|
|
|
|
const handleChangeSelectedTeam = (id: number) => {
|
|
|
|
|
const { MANAGE_POLICIES } = PATHS;
|
|
|
|
|
const path = id ? `${MANAGE_POLICIES}?team_id=${id}` : MANAGE_POLICIES;
|
|
|
|
|
router.replace(path);
|
|
|
|
|
setShowInheritedPolicies(false);
|
|
|
|
|
setSelectedPolicyIds([]);
|
|
|
|
|
};
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const toggleRemovePoliciesModal = () =>
|
2021-08-30 23:02:53 +00:00
|
|
|
|
setShowRemovePoliciesModal(!showRemovePoliciesModal);
|
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
const toggleShowInheritedPolicies = () =>
|
|
|
|
|
setShowInheritedPolicies(!showInheritedPolicies);
|
|
|
|
|
|
|
|
|
|
const onRemovePoliciesClick = (selectedTableIds: number[]): void => {
|
|
|
|
|
toggleRemovePoliciesModal();
|
|
|
|
|
setSelectedPolicyIds(selectedTableIds);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onRemovePoliciesSubmit = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const request = selectedTeamId
|
|
|
|
|
? teamPoliciesAPI.destroy(selectedTeamId, selectedPolicyIds)
|
|
|
|
|
: globalPoliciesAPI.destroy(selectedPolicyIds);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
await request.then(() => {
|
2021-08-30 23:02:53 +00:00
|
|
|
|
dispatch(
|
|
|
|
|
renderFlash(
|
|
|
|
|
"success",
|
|
|
|
|
`Successfully removed ${
|
2021-09-30 19:32:06 +00:00
|
|
|
|
selectedPolicyIds?.length === 1 ? "policy" : "policies"
|
2021-08-30 23:02:53 +00:00
|
|
|
|
}.`
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
2021-09-30 19:32:06 +00:00
|
|
|
|
} catch {
|
|
|
|
|
dispatch(
|
|
|
|
|
renderFlash(
|
|
|
|
|
"error",
|
|
|
|
|
`Unable to remove ${
|
|
|
|
|
selectedPolicyIds?.length === 1 ? "policy" : "policies"
|
|
|
|
|
}. Please try again.`
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
toggleRemovePoliciesModal();
|
|
|
|
|
getPolicies(selectedTeamId);
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-09-30 19:32:06 +00:00
|
|
|
|
// Sort list of teams the current user has permission to access and set as userTeams.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isPremiumTier) {
|
|
|
|
|
let unsortedTeams: ITeam[] | null = null;
|
|
|
|
|
if (isOnGlobalTeam && teams) {
|
|
|
|
|
unsortedTeams = teams;
|
|
|
|
|
} else if (!isOnGlobalTeam && currentUser?.teams) {
|
|
|
|
|
unsortedTeams = currentUser.teams;
|
|
|
|
|
}
|
|
|
|
|
if (unsortedTeams !== null) {
|
2021-10-02 00:35:13 +00:00
|
|
|
|
const sortedTeams = unsortedTeams.sort((a, b) =>
|
|
|
|
|
sortUtils.caseInsensitiveAsc(a.name, b.name)
|
2021-08-30 23:02:53 +00:00
|
|
|
|
);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
setUserTeams(sortedTeams);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
}
|
|
|
|
|
}, [currentUser, isOnGlobalTeam, isPremiumTier, teams]);
|
|
|
|
|
|
|
|
|
|
// Watch the location url and parse team param to set selectedTeamId.
|
|
|
|
|
// Note 0 is used as the id for the "All teams" option.
|
|
|
|
|
// Null case is used to represent no valid id has been selected.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
let teamId: number | null = parseInt(location?.query?.team_id, 10) || 0;
|
|
|
|
|
|
|
|
|
|
// If the team id does not match one in the user teams list,
|
|
|
|
|
// we use a default value and change call change handler
|
|
|
|
|
// to update url params with the default value.
|
|
|
|
|
// We return early to guard against potential invariant condition.
|
|
|
|
|
if (userTeams && !userTeams.find((t) => t.id === teamId)) {
|
|
|
|
|
if (isOnGlobalTeam) {
|
|
|
|
|
// For global users, default to zero (i.e. all teams).
|
|
|
|
|
if (teamId !== 0) {
|
|
|
|
|
handleChangeSelectedTeam(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// For non-global users, default to the first team in the list.
|
|
|
|
|
// If there is no default team, set teamId to null so that getPolicies
|
|
|
|
|
// API request will not be triggered.
|
|
|
|
|
teamId = userTeams[0]?.id || null;
|
|
|
|
|
if (teamId) {
|
|
|
|
|
handleChangeSelectedTeam(teamId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Null case must be distinguished from 0 (which is used as the id for the "All teams" option)
|
|
|
|
|
// 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);
|
2021-10-02 00:35:13 +00:00
|
|
|
|
}, [isOnGlobalTeam, location, userTeams]);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
|
|
|
|
|
// Watch for selected team changes and call getPolicies to make new policies API request.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// Null case must be distinguished from 0 (which is used as the id for the "All teams" option)
|
|
|
|
|
// so a falsiness check cannot be used here. Null case here allows us to skip API call.
|
|
|
|
|
if (selectedTeamId !== null) {
|
2021-10-26 20:55:53 +00:00
|
|
|
|
if (isOnGlobalTeam || isAnyTeamMaintainerOrTeamAdmin) {
|
2021-09-30 19:32:06 +00:00
|
|
|
|
getGlobalPolicies();
|
|
|
|
|
}
|
|
|
|
|
if (selectedTeamId) {
|
|
|
|
|
getTeamPolicies(selectedTeamId);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-02 00:35:13 +00:00
|
|
|
|
}, [
|
|
|
|
|
getGlobalPolicies,
|
|
|
|
|
getTeamPolicies,
|
2021-10-26 20:55:53 +00:00
|
|
|
|
isAnyTeamMaintainerOrTeamAdmin,
|
2021-10-02 00:35:13 +00:00
|
|
|
|
isOnGlobalTeam,
|
|
|
|
|
selectedTeamId,
|
|
|
|
|
]);
|
2021-09-30 19:32:06 +00:00
|
|
|
|
|
2021-10-11 15:13:10 +00:00
|
|
|
|
// Pull osquery policy update interval value from config, reformat, and set as updateInterval.
|
2021-09-30 19:32:06 +00:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (config) {
|
2021-10-11 15:13:10 +00:00
|
|
|
|
const { osquery_policy: interval } = config;
|
2021-09-30 19:32:06 +00:00
|
|
|
|
interval &&
|
|
|
|
|
setUpdateInterval(secondsToHms(inMilliseconds(interval) / 1000));
|
|
|
|
|
}
|
|
|
|
|
}, [config]);
|
2021-08-30 23:02:53 +00:00
|
|
|
|
|
2021-10-02 00:35:13 +00:00
|
|
|
|
// 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 =
|
2021-10-06 17:30:17 +00:00
|
|
|
|
!!selectedTeamId && !!globalPolicies?.length && !isGlobalPoliciesError;
|
2021-10-02 00:35:13 +00:00
|
|
|
|
|
2021-11-10 18:46:16 +00:00
|
|
|
|
const selectedTeamData = userTeams?.find(
|
|
|
|
|
(team) => selectedTeamId === team.id
|
|
|
|
|
);
|
|
|
|
|
|
2021-08-30 23:02:53 +00:00
|
|
|
|
return (
|
|
|
|
|
<div className={baseClass}>
|
|
|
|
|
<div className={`${baseClass}__wrapper body-wrap`}>
|
|
|
|
|
<div className={`${baseClass}__header-wrap`}>
|
|
|
|
|
<div className={`${baseClass}__header`}>
|
|
|
|
|
<div className={`${baseClass}__text`}>
|
2021-09-30 19:32:06 +00:00
|
|
|
|
<div className={`${baseClass}__title`}>
|
|
|
|
|
{isFreeTier && <h1>Policies</h1>}
|
|
|
|
|
{isPremiumTier &&
|
|
|
|
|
userTeams !== null &&
|
|
|
|
|
selectedTeamId !== null && (
|
|
|
|
|
<TeamsDropdown
|
|
|
|
|
currentUserTeams={userTeams}
|
|
|
|
|
onChange={handleChangeSelectedTeam}
|
|
|
|
|
selectedTeam={selectedTeamId}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2021-08-30 23:02:53 +00:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-09-30 19:32:06 +00:00
|
|
|
|
{canAddOrRemovePolicy(currentUser, selectedTeamId) && (
|
2021-08-30 23:02:53 +00:00
|
|
|
|
<div className={`${baseClass}__action-button-container`}>
|
2021-11-24 17:16:42 +00:00
|
|
|
|
<Link
|
|
|
|
|
to={PATHS.NEW_POLICY}
|
|
|
|
|
className={`${baseClass}__add-policy-link`}
|
2021-08-30 23:02:53 +00:00
|
|
|
|
>
|
|
|
|
|
Add a policy
|
2021-11-24 17:16:42 +00:00
|
|
|
|
</Link>
|
2021-08-30 23:02:53 +00:00
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className={`${baseClass}__description`}>
|
2021-09-30 19:32:06 +00:00
|
|
|
|
{isPremiumTier && !!selectedTeamId && (
|
2021-08-30 23:02:53 +00:00
|
|
|
|
<p>
|
2021-09-30 19:32:06 +00:00
|
|
|
|
Add additional policies for <b>all hosts assigned to this team</b>
|
2021-08-30 23:02:53 +00:00
|
|
|
|
.
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
2021-10-02 00:35:13 +00:00
|
|
|
|
{showDefaultDescription && (
|
|
|
|
|
<p>
|
|
|
|
|
Add policies for <b>all of your hosts</b> to see which pass your
|
|
|
|
|
organization’s standards.{" "}
|
|
|
|
|
</p>
|
2021-09-30 19:32:06 +00:00
|
|
|
|
)}
|
2021-10-02 00:35:13 +00:00
|
|
|
|
</div>
|
|
|
|
|
{!!updateInterval && showInfoBanner && (
|
|
|
|
|
<InfoBanner className={`${baseClass}__sandbox-info`}>
|
|
|
|
|
<p>
|
|
|
|
|
Your policies are checked every <b>{updateInterval.trim()}</b>.{" "}
|
|
|
|
|
{isGlobalAdmin && (
|
|
|
|
|
<span>
|
|
|
|
|
Check out the Fleet documentation on{" "}
|
|
|
|
|
<a href={DOCS_LINK} target="_blank" rel="noreferrer">
|
|
|
|
|
<b>how to edit this frequency</b>
|
|
|
|
|
</a>
|
|
|
|
|
.
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</p>
|
|
|
|
|
</InfoBanner>
|
|
|
|
|
)}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
<div>
|
|
|
|
|
{!!selectedTeamId &&
|
|
|
|
|
(isTeamPoliciesError ? (
|
|
|
|
|
<TableDataError />
|
|
|
|
|
) : (
|
|
|
|
|
<PoliciesListWrapper
|
|
|
|
|
policiesList={teamPolicies}
|
|
|
|
|
isLoading={isLoadingTeamPolicies}
|
|
|
|
|
onRemovePoliciesClick={onRemovePoliciesClick}
|
|
|
|
|
canAddOrRemovePolicy={canAddOrRemovePolicy(
|
|
|
|
|
currentUser,
|
|
|
|
|
selectedTeamId
|
|
|
|
|
)}
|
2021-11-10 18:46:16 +00:00
|
|
|
|
selectedTeamData={selectedTeamData}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
{!selectedTeamId &&
|
|
|
|
|
(isGlobalPoliciesError ? (
|
|
|
|
|
<TableDataError />
|
|
|
|
|
) : (
|
|
|
|
|
<PoliciesListWrapper
|
|
|
|
|
policiesList={globalPolicies}
|
|
|
|
|
isLoading={isLoadingGlobalPolicies}
|
|
|
|
|
onRemovePoliciesClick={onRemovePoliciesClick}
|
|
|
|
|
canAddOrRemovePolicy={canAddOrRemovePolicy(
|
|
|
|
|
currentUser,
|
|
|
|
|
selectedTeamId
|
|
|
|
|
)}
|
2021-11-10 18:46:16 +00:00
|
|
|
|
selectedTeamData={selectedTeamData}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
/>
|
|
|
|
|
))}
|
2021-08-30 23:02:53 +00:00
|
|
|
|
</div>
|
2021-10-02 00:35:13 +00:00
|
|
|
|
{showInheritedPoliciesButton && (
|
|
|
|
|
<span>
|
|
|
|
|
<Button
|
|
|
|
|
variant="unstyled"
|
|
|
|
|
className={`${showInheritedPolicies ? "upcarat" : "rightcarat"}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
${baseClass}__inherited-policies-button`}
|
2021-10-02 00:35:13 +00:00
|
|
|
|
onClick={toggleShowInheritedPolicies}
|
|
|
|
|
>
|
|
|
|
|
{renderInheritedPoliciesButtonText(
|
|
|
|
|
showInheritedPolicies,
|
|
|
|
|
globalPolicies
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
<div className={`${baseClass}__details`}>
|
|
|
|
|
<IconToolTip
|
|
|
|
|
isHtml
|
|
|
|
|
text={
|
|
|
|
|
"\
|
2021-09-30 19:32:06 +00:00
|
|
|
|
<center><p>“All teams” policies are checked <br/> for this team’s hosts.</p></center>\
|
|
|
|
|
"
|
2021-10-02 00:35:13 +00:00
|
|
|
|
}
|
2021-09-30 19:32:06 +00:00
|
|
|
|
/>
|
|
|
|
|
</div>
|
2021-10-02 00:35:13 +00:00
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{showInheritedPoliciesButton && showInheritedPolicies && (
|
|
|
|
|
<div className={`${baseClass}__inherited-policies-table`}>
|
|
|
|
|
<PoliciesListWrapper
|
|
|
|
|
isLoading={isLoadingGlobalPolicies}
|
|
|
|
|
policiesList={globalPolicies}
|
|
|
|
|
onRemovePoliciesClick={noop}
|
|
|
|
|
resultsTitle="policies"
|
|
|
|
|
canAddOrRemovePolicy={canAddOrRemovePolicy(
|
|
|
|
|
currentUser,
|
|
|
|
|
selectedTeamId
|
|
|
|
|
)}
|
|
|
|
|
tableType="inheritedPolicies"
|
2021-11-10 18:46:16 +00:00
|
|
|
|
selectedTeamData={selectedTeamData}
|
2021-10-02 00:35:13 +00:00
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2021-08-30 23:02:53 +00:00
|
|
|
|
{showRemovePoliciesModal && (
|
|
|
|
|
<RemovePoliciesModal
|
|
|
|
|
onCancel={toggleRemovePoliciesModal}
|
|
|
|
|
onSubmit={onRemovePoliciesSubmit}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default ManagePolicyPage;
|