fleet/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/DiskEncryption.tsx
jacobshandling ea9b5ba776
UI – 11/26 Disk encryption spec updates (#24175)
## #24173 

<img width="1390" alt="Screenshot 2024-11-26 at 11 08 25 AM"
src="https://github.com/user-attachments/assets/3afc7031-2bac-4cc9-ac8d-e47390a0ef93">
<img width="1392" alt="Screenshot 2024-11-26 at 10 45 11 AM"
src="https://github.com/user-attachments/assets/2eccc992-e0bd-4c5c-b001-75cde60317b5">

- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2024-11-26 11:29:50 -08:00

229 lines
6.6 KiB
TypeScript

import React, { useContext, useState } from "react";
import { useQuery } from "react-query";
import { InjectedRouter } from "react-router";
import { AppContext } from "context/app";
import { NotificationContext } from "context/notification";
import { ITeamConfig } from "interfaces/team";
import { getErrorReason } from "interfaces/errors";
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
import diskEncryptionAPI from "services/entities/disk_encryption";
import teamsAPI, { ILoadTeamResponse } from "services/entities/teams";
import configAPI from "services/entities/config";
import Button from "components/buttons/Button";
import CustomLink from "components/CustomLink";
import Checkbox from "components/forms/fields/Checkbox";
import PremiumFeatureMessage from "components/PremiumFeatureMessage";
import Spinner from "components/Spinner";
import SectionHeader from "components/SectionHeader";
import TooltipWrapper from "components/TooltipWrapper";
import DiskEncryptionTable from "./components/DiskEncryptionTable";
const baseClass = "disk-encryption";
interface IDiskEncryptionProps {
currentTeamId: number;
onMutation: () => void;
router: InjectedRouter;
}
const DiskEncryption = ({
currentTeamId,
onMutation,
router,
}: IDiskEncryptionProps) => {
const { isPremiumTier, config, setConfig } = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);
const defaultShowDiskEncryption = currentTeamId
? false
: config?.mdm.enable_disk_encryption ?? false;
const [isLoadingTeam, setIsLoadingTeam] = useState(true);
const [showAggregate, setShowAggregate] = useState(defaultShowDiskEncryption);
const [diskEncryptionEnabled, setDiskEncryptionEnabled] = useState(
defaultShowDiskEncryption
);
// because we pull the default state for no teams from the config,
// we need to update the config when the user toggles the checkbox
const getUpdatedAppConfig = async () => {
try {
const updatedConfig = await configAPI.loadAll();
setConfig(updatedConfig);
} catch {
renderFlash(
"error",
"Could not retrieve updated app config. Please try again."
);
}
};
const onToggleCheckbox = (value: boolean) => {
setDiskEncryptionEnabled(value);
};
useQuery<ILoadTeamResponse, Error, ITeamConfig>(
["team", currentTeamId],
() => teamsAPI.load(currentTeamId),
{
refetchOnWindowFocus: false,
retry: false,
enabled: currentTeamId !== 0,
select: (res) => res.team,
onSuccess: (res) => {
const enableDiskEncryption = res.mdm?.enable_disk_encryption ?? false;
setDiskEncryptionEnabled(enableDiskEncryption);
setShowAggregate(enableDiskEncryption);
setIsLoadingTeam(false);
},
}
);
const onUpdateDiskEncryption = async () => {
try {
await diskEncryptionAPI.updateDiskEncryption(
diskEncryptionEnabled,
currentTeamId
);
renderFlash(
"success",
"Successfully updated disk encryption enforcement!"
);
onMutation();
setShowAggregate(diskEncryptionEnabled);
if (currentTeamId === 0) {
getUpdatedAppConfig();
}
} catch (e) {
if (getErrorReason(e).includes("Missing required private key")) {
const link =
"https://fleetdm.com/learn-more-about/fleet-server-private-key";
renderFlash(
"error",
<>
Could&apos;t enable disk encryption. Missing required private key.
Learn how to configure the private key here:{" "}
<a href={link}>{link}</a>
</>
);
} else {
renderFlash(
"error",
"Could not update the disk encryption enforcement. Please try again."
);
}
}
};
if (currentTeamId === 0 && isLoadingTeam) {
setIsLoadingTeam(false);
}
const getTipContent = (platform: "windows" | "macOS" | "linux") => {
if (platform === "linux") {
return (
<>
For Ubuntu and Fedora Linux.
<br />
Currently, full disk encryption must be turned on{" "}
<b>
during OS
<br />
setup
</b>
. If disk encryption is off, the end user must re-install
<br />
their operating system.
</>
);
}
const [AppleOrWindows, DEMethod] =
platform === "windows"
? ["Windows", "BitLocker"]
: ["Apple", "FileVault"];
return (
<>
{AppleOrWindows} MDM must be turned on in{" "}
<a href="/settings/integrations/mdm">
<b>Settings</b> &gt; <b>Integrations</b> &gt;{" "}
<b>Mobile Device Management (MDM)</b>
</a>{" "}
to enforce disk encryption via {DEMethod}.
</>
);
};
const subTitle = (
<>
Disk encryption is available on{" "}
<TooltipWrapper tipContent={getTipContent("macOS")}>macOS</TooltipWrapper>
,{" "}
<TooltipWrapper tipContent={getTipContent("windows")}>
Windows
</TooltipWrapper>
, and{" "}
<TooltipWrapper tipContent={getTipContent("linux")}>Linux</TooltipWrapper>{" "}
hosts.
</>
);
return (
<div className={baseClass}>
<SectionHeader
title="Disk encryption"
subTitle={subTitle}
alignLeftHeaderVertically
greySubtitle
/>
{!isPremiumTier ? (
<PremiumFeatureMessage
className={`${baseClass}__premium-feature-message`}
/>
) : (
<>
{isLoadingTeam ? (
<Spinner />
) : (
<div className="disk-encryption-content">
{showAggregate && (
<DiskEncryptionTable
currentTeamId={currentTeamId}
router={router}
/>
)}
<Checkbox
onChange={onToggleCheckbox}
value={diskEncryptionEnabled}
className={`${baseClass}__checkbox`}
>
Turn on disk encryption
</Checkbox>
<p>
If turned on, hosts&apos; disk encryption keys will be stored in
Fleet{" "}
<CustomLink
text="Learn more"
url={`${LEARN_MORE_ABOUT_BASE_LINK}/mdm-disk-encryption`}
newTab
/>
</p>
<Button
className={`${baseClass}__save-button`}
onClick={onUpdateDiskEncryption}
>
Save
</Button>
</div>
)}
</>
)}
</div>
);
};
export default DiskEncryption;