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:
co-authored by
Jacob Shandling
parent
6b985da9e1
commit
ce5a1b44cd
@@ -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.
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -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 = ({
|
||||
don’t 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}
|
||||
|
||||
Reference in New Issue
Block a user