UI: Disk encryption banners (#10329)

## Addresses #9414
## Implements
<img width="1021" alt="Screenshot 2023-03-06 at 5 47 18 PM"
src="https://user-images.githubusercontent.com/61553566/223297991-507da9c5-1dd6-40da-b16b-9961645abd4e.png">
<img width="1257" alt="Screenshot 2023-03-06 at 5 47 49 PM"
src="https://user-images.githubusercontent.com/61553566/223298063-5ff380ed-7006-40a7-8a81-3a42936fa435.png">


https://user-images.githubusercontent.com/61553566/223298116-eabbb73c-6323-49b7-b731-3b5da2e3d28d.mov

## Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`
- [ ] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
Jacob Shandling
2023-03-13 14:15:55 -07:00
committed by GitHub
co-authored by Jacob Shandling
parent 6b985da9e1
commit ce5a1b44cd
12 changed files with 179 additions and 9 deletions
+3
View File
@@ -0,0 +1,3 @@
- Add information banners on the Host Details and My Device pages that appear when the user must
either reset their encryption (FileVault on macOS) key, or logout/restart, to enable disk
encryption.
+5
View File
@@ -36,6 +36,11 @@ const DEFAULT_HOST_MOCK: IHost = {
encryption_key_available: false,
enrollment_status: "Off",
server_url: "https://www.example.com/1",
profiles: [],
macos_settings: {
disk_encryption: null,
action_required: null,
},
},
public_ip: "",
primary_ip: "172.23.0.3",
+5
View File
@@ -8,6 +8,11 @@ const DEFAULT_MAC_ADMINS_MOCK: IMacadminsResponse = {
server_url: "https://kandji.com/2",
name: "Kandji",
id: 11,
profiles: [],
macos_settings: {
disk_encryption: null,
action_required: null,
},
},
munki: {
version: "1.2.3",
+5
View File
@@ -20,6 +20,11 @@ const DEFAULT_HOST_MDM_DATA: IHostMdmData = {
server_url: "http://mdmsolution.com",
name: "MDM Solution",
id: 1,
profiles: [],
macos_settings: {
disk_encryption: null,
action_required: null,
},
};
export const createMockHostMdmData = (
+19 -3
View File
@@ -86,13 +86,29 @@ export interface IMunkiData {
version: string;
}
type MacDiskEncryptionState =
| "applied"
| "action_required"
| "enforcing"
| "failed"
| "removing_enforcement"
| null;
type MacDiskEncryptionActionRequired = "log_out" | "rotate_key" | null;
interface IMdmMacOsSettings {
disk_encryption: MacDiskEncryptionState;
action_required: MacDiskEncryptionActionRequired;
}
export interface IHostMdmData {
encryption_key_available: boolean;
enrollment_status: MdmEnrollmentStatus | null;
server_url: string;
profiles?: IMacSettings;
id?: number;
name?: string;
server_url: string;
id?: number;
profiles: IMacSettings;
macos_settings: IMdmMacOsSettings;
}
export interface IMunkiIssue {
@@ -42,6 +42,7 @@ import PolicyDetailsModal from "../cards/Policies/HostPoliciesTable/PolicyDetail
import AutoEnrollMdmModal from "./AutoEnrollMdmModal";
import ManualEnrollMdmModal from "./ManualEnrollMdmModal";
import MacSettingsModal from "../MacSettingsModal";
import ResetKeyModal from "./ResetKeyModal";
const baseClass = "device-user";
@@ -63,6 +64,7 @@ const DeviceUserPage = ({
const [isPremiumTier, setIsPremiumTier] = useState(false);
const [showInfoModal, setShowInfoModal] = useState(false);
const [showEnrollMdmModal, setShowEnrollMdmModal] = useState(false);
const [showResetKeyModal, setShowResetKeyModal] = useState(false);
const [refetchStartTime, setRefetchStartTime] = useState<number | null>(null);
const [showRefetchSpinner, setShowRefetchSpinner] = useState(false);
const [hostSoftware, setHostSoftware] = useState<ISoftware[]>([]);
@@ -95,7 +97,7 @@ const DeviceUserPage = ({
}
);
const { data: deviceMacAdminsData, refetch: refetchMacadmins } = useQuery(
const { data: deviceMacAdminsData } = useQuery(
["macadmins", deviceAuthToken],
() => deviceUserAPI.loadHostDetailsExtension(deviceAuthToken, "macadmins"),
{
@@ -229,6 +231,10 @@ const DeviceUserPage = ({
setShowEnrollMdmModal(!showEnrollMdmModal);
}, [showEnrollMdmModal, setShowEnrollMdmModal]);
const toggleResetKeyModal = useCallback(() => {
setShowResetKeyModal(!showResetKeyModal);
}, [showResetKeyModal, setShowResetKeyModal]);
const togglePolicyDetailsModal = useCallback(
(policy: IHostPolicy) => {
setShowPolicyDetailsModal(!showPolicyDetailsModal);
@@ -295,10 +301,29 @@ const DeviceUserPage = ({
);
};
const resetKeyButton = (
<Button variant="unstyled" onClick={toggleResetKeyModal}>
<b>Reset key</b>
</Button>
);
const renderDeviceUserPage = () => {
const failingPoliciesCount = host?.issues?.failing_policies_count || 0;
const isMdmUnenrolled =
host?.mdm.enrollment_status === "Off" || !host?.mdm.enrollment_status;
const diskEncryptionBannersEnabled =
globalConfig?.mdm.enabled_and_configured && host?.mdm.name === "Fleet";
const showDiskEncryptionLogoutRestart =
diskEncryptionBannersEnabled &&
host?.mdm.macos_settings.disk_encryption === "action_required" &&
host?.mdm.macos_settings.action_required === "log_out";
const showDiskEncryptionKeyResetRequired =
diskEncryptionBannersEnabled &&
host?.mdm.macos_settings.disk_encryption === "action_required" &&
host?.mdm.macos_settings.action_required === "rotate_key";
return (
<div className="fleet-desktop-wrapper">
{isLoadingHost ? (
@@ -308,6 +333,7 @@ const DeviceUserPage = ({
{host?.platform === "darwin" &&
isMdmUnenrolled &&
globalConfig?.mdm.enabled_and_configured && (
// Turn on MDM banner
<InfoBanner color="yellow" cta={turnOnMdmButton} pageLevel>
Mobile device management (MDM) is off. MDM allows your
organization to change settings and install software. This
@@ -315,6 +341,22 @@ const DeviceUserPage = ({
dont have to.
</InfoBanner>
)}
{showDiskEncryptionLogoutRestart && (
// MDM - Disk Encryption: Logout or restart banner
<InfoBanner color="yellow">
Disk encryption: Log out of your device or restart to turn on
disk encryption. This prevents unauthorized access to the
information on your device.
</InfoBanner>
)}
{showDiskEncryptionKeyResetRequired && (
// MDM - Disk Encryption: Reset key required banner
<InfoBanner color="yellow" cta={resetKeyButton}>
Disk encryption: Reset your disk encryption key. This lets your
organization help you unlock your device if you forget your
password.
</InfoBanner>
)}
<HostSummaryCard
statusClassName={statusClassName}
titleData={titleData}
@@ -373,6 +415,9 @@ const DeviceUserPage = ({
</TabsWrapper>
{showInfoModal && <InfoModal onCancel={toggleInfoModal} />}
{showEnrollMdmModal && renderEnrollMdmModal()}
{showResetKeyModal && (
<ResetKeyModal onCancel={toggleResetKeyModal} />
)}
</div>
)}
{!!host && showPolicyDetailsModal && (
@@ -1,5 +0,0 @@
.enroll-mdm-modal {
&__download-button {
margin-top: 12px;
}
}
@@ -0,0 +1,59 @@
import React, { useState } from "react";
import Button from "components/buttons/Button";
import Modal from "components/Modal";
interface IResetKeyModalProps {
onCancel: () => void;
}
const baseClass = "reset-key-modal";
const ResetKeyModal = ({ onCancel }: IResetKeyModalProps): JSX.Element => {
const [success, setSuccess] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
// TODO: actually make this work: https://www.figma.com/file/hdALBDsrti77QuDNSzLdkx/%F0%9F%9A%A7-Fleet-EE-(dev-ready%2C-scratchpad)?node-id=11728%3A323033&t=GbmGwTkgjENhmJmO-1
const startNativeKeyReset = () => {
setIsLoading(true);
setTimeout(() => {
setSuccess(true);
}, 1000);
};
return (
<Modal title="Reset key" onExit={onCancel} className={baseClass}>
<div>
<ol>
<li>
Click <b>Start</b> and enter your username and password.
{success ? (
<div className={`${baseClass}__success`}>Success!</div>
) : (
<Button
type="button"
onClick={startNativeKeyReset}
variant="brand"
className={`${baseClass}__start-button`}
isLoading={isLoading}
>
Start
</Button>
)}
</li>
<li>
Close this window and select <b>Refetch</b> on your My device page.
This tells your organization that you reset your key.
</li>
</ol>
<div className="modal-cta-wrap">
<Button type="button" onClick={onCancel} variant="brand">
Done
</Button>
</div>
</div>
</Modal>
);
};
export default ResetKeyModal;
@@ -0,0 +1,20 @@
.reset-key-modal {
&__start-button {
margin-top: 12px;
}
&__success {
margin-top: 12px;
padding: $pad-small;
height: 22px;
}
ol {
padding-left: 0;
}
li {
margin-bottom: $pad-large;
list-style: number inside;
}
}
@@ -0,0 +1 @@
export { default } from "./ResetKeyModal";
@@ -20,6 +20,10 @@
margin: $pad-large 0;
}
&__download-button {
margin-top: 12px;
}
ol {
padding-left: 0;
}
@@ -600,6 +600,11 @@ const HostDetailsPage = ({
const isMdmUnenrolled =
host?.mdm.enrollment_status === "Off" || !host?.mdm.enrollment_status;
const showDiskEncryptionUserActionRequired =
config?.mdm.enabled_and_configured &&
host?.mdm.name === "Fleet" &&
host?.mdm.macos_settings.disk_encryption === "action_required";
return (
<MainContent className={baseClass}>
<div className={`${baseClass}__wrapper`}>
@@ -613,6 +618,13 @@ const HostDetailsPage = ({
<strong>My device</strong> page.
</InfoBanner>
)}
{showDiskEncryptionUserActionRequired && (
<InfoBanner color="yellow">
Disk encryption: Requires action from the end user. Ask the end
user to follow <b>Disk encryption</b> instructions on their{" "}
<b>My device</b> page.
</InfoBanner>
)}
<BackLink
text="Back to all hosts"
path={filteredHostsPath || PATHS.MANAGE_HOSTS}