mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
UI: Add automatic EnrollMdm modal (#9455)
# Addresses #9365 # Implements MDM enrollment modal that handles both automatic and manual enrollment instructions: - Automatic: <img width="1181" alt="Screenshot 2023-01-20 at 4 33 50 PM" src="https://user-images.githubusercontent.com/61553566/213829293-6d4a5053-9a3c-4f52-8cf8-a6607dc8df4e.png"> - Manual: <img width="1158" alt="Screenshot 2023-01-20 at 4 35 04 PM" src="https://user-images.githubusercontent.com/61553566/213829369-73ae779d-14a8-4aa7-9c6a-b97d046d0dc1.png"> - Also includes (by mistake, but might as well include them now) some small bash scripts for use in MDM development # 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/` - [x] Updated testing inventory - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
parent
d3565dc032
commit
60712144f2
12 changed files with 158 additions and 46 deletions
1
changes/9365-add-automatic-MDM-enrollment-modal
Normal file
1
changes/9365-add-automatic-MDM-enrollment-modal
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Add modal for automatic enrollment of a macOS host to MDM
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import React from "react";
|
||||
|
||||
import Button from "components/buttons/Button";
|
||||
import Modal from "components/Modal";
|
||||
|
||||
interface IAutoEnrollMdmModalProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const baseClass = "auto-enroll-mdm-modal enroll-mdm-modal";
|
||||
|
||||
const AutoEnrollMdmModal = ({
|
||||
onCancel,
|
||||
}: IAutoEnrollMdmModalProps): JSX.Element => {
|
||||
return (
|
||||
<Modal title="Turn on MDM" onExit={onCancel} className={baseClass}>
|
||||
<div>
|
||||
<p className={`${baseClass}__description`}>
|
||||
To turn on MDM, Apple Inc. requires that you install a profile.
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
From the Apple menu in the top left corner of your screen, select{" "}
|
||||
<b>System Settings</b> or <b>System Preferences</b>.
|
||||
</li>
|
||||
<li>
|
||||
In the search bar, type “Profiles.” Select <b>Profiles</b>, find and
|
||||
select <b>Enrollment Profile</b>, and select <b>Install</b>.
|
||||
</li>
|
||||
<li>
|
||||
Enter your password, and select <b>Enroll</b>.
|
||||
</li>
|
||||
<li>
|
||||
Close this window and select <b>Refetch</b> on your My device page
|
||||
to tell your organization that MDM is on.
|
||||
</li>
|
||||
</ol>
|
||||
<div className="modal-cta-wrap">
|
||||
<Button type="button" onClick={onCancel} variant="brand">
|
||||
Done
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoEnrollMdmModal;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./AutoEnrollMdmModal";
|
||||
|
|
@ -34,11 +34,12 @@ import AboutCard from "../cards/About";
|
|||
import SoftwareCard from "../cards/Software";
|
||||
import PoliciesCard from "../cards/Policies";
|
||||
import InfoModal from "./InfoModal";
|
||||
import ManualEnrollMdmModal from "./ManualEnrollMdmModal";
|
||||
|
||||
import InfoIcon from "../../../../../assets/images/icon-info-purple-14x14@2x.png";
|
||||
import FleetIcon from "../../../../../assets/images/fleet-avatar-24x24@2x.png";
|
||||
import PolicyDetailsModal from "../cards/Policies/HostPoliciesTable/PolicyDetailsModal";
|
||||
import AutoEnrollMdmModal from "./AutoEnrollMdmModal";
|
||||
import ManualEnrollMdmModal from "./ManualEnrollMdmModal";
|
||||
|
||||
const baseClass = "device-user";
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ const DeviceUserPage = ({
|
|||
|
||||
const [isPremiumTier, setIsPremiumTier] = useState(false);
|
||||
const [showInfoModal, setShowInfoModal] = useState(false);
|
||||
const [showMdmModal, setShowMdmModal] = useState(false);
|
||||
const [showEnrollMdmModal, setShowEnrollMdmModal] = useState(false);
|
||||
const [refetchStartTime, setRefetchStartTime] = useState<number | null>(null);
|
||||
const [showRefetchSpinner, setShowRefetchSpinner] = useState(false);
|
||||
const [hostSoftware, setHostSoftware] = useState<ISoftware[]>([]);
|
||||
|
|
@ -215,9 +216,9 @@ const DeviceUserPage = ({
|
|||
setShowInfoModal(!showInfoModal);
|
||||
}, [showInfoModal, setShowInfoModal]);
|
||||
|
||||
const toggleTurnOnMdmModal = useCallback(() => {
|
||||
setShowMdmModal(!showMdmModal);
|
||||
}, [showMdmModal, setShowMdmModal]);
|
||||
const toggleEnrollMdmModal = useCallback(() => {
|
||||
setShowEnrollMdmModal(!showEnrollMdmModal);
|
||||
}, [showEnrollMdmModal, setShowEnrollMdmModal]);
|
||||
|
||||
const togglePolicyDetailsModal = useCallback(
|
||||
(policy: IHostPolicy) => {
|
||||
|
|
@ -264,10 +265,22 @@ const DeviceUserPage = ({
|
|||
const statusClassName = classnames("status", `status--${host?.status}`);
|
||||
|
||||
const turnOnMdmButton = (
|
||||
<Button variant="unstyled" onClick={() => setShowMdmModal(true)}>
|
||||
<Button variant="unstyled" onClick={toggleEnrollMdmModal}>
|
||||
<b>Turn on MDM</b>
|
||||
</Button>
|
||||
);
|
||||
|
||||
const renderEnrollMdmModal = () => {
|
||||
return host?.mdm_enrollment_status === "Pending" ? (
|
||||
<AutoEnrollMdmModal onCancel={toggleEnrollMdmModal} />
|
||||
) : (
|
||||
<ManualEnrollMdmModal
|
||||
onCancel={toggleEnrollMdmModal}
|
||||
token={deviceAuthToken}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDeviceUserPage = () => {
|
||||
const failingPoliciesCount = host?.issues?.failing_policies_count || 0;
|
||||
return (
|
||||
|
|
@ -339,12 +352,7 @@ const DeviceUserPage = ({
|
|||
</Tabs>
|
||||
</TabsWrapper>
|
||||
{showInfoModal && <InfoModal onCancel={toggleInfoModal} />}
|
||||
{showMdmModal && (
|
||||
<ManualEnrollMdmModal
|
||||
onCancel={toggleTurnOnMdmModal}
|
||||
token={deviceAuthToken}
|
||||
/>
|
||||
)}
|
||||
{showEnrollMdmModal && renderEnrollMdmModal()}
|
||||
</div>
|
||||
)}
|
||||
{!!host && showPolicyDetailsModal && (
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ import Spinner from "components/Spinner";
|
|||
|
||||
import mdmAPI from "services/entities/mdm";
|
||||
|
||||
export interface IInfoModalProps {
|
||||
interface IManualEnrollMdmModalProps {
|
||||
onCancel: () => void;
|
||||
token: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
const baseClass = "manual-enroll-mdm-modal";
|
||||
const baseClass = "manual-enroll-mdm-modal enroll-mdm-modal";
|
||||
|
||||
const ManualEnrollMdmModal = ({
|
||||
onCancel,
|
||||
token,
|
||||
}: IInfoModalProps): JSX.Element => {
|
||||
token = "",
|
||||
}: IManualEnrollMdmModalProps): JSX.Element => {
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
|
||||
const [isDownloadingProfile, setIsDownloadingProfile] = useState(false);
|
||||
|
|
@ -60,15 +60,15 @@ const ManualEnrollMdmModal = ({
|
|||
|
||||
return false;
|
||||
};
|
||||
const renderModalContent = () => {
|
||||
if (isFetchingMdmProfile) {
|
||||
return <Spinner />;
|
||||
}
|
||||
if (fetchMdmProfileError) {
|
||||
return <DataError card />;
|
||||
}
|
||||
if (isFetchingMdmProfile) {
|
||||
return <Spinner />;
|
||||
}
|
||||
if (fetchMdmProfileError) {
|
||||
return <DataError card />;
|
||||
}
|
||||
|
||||
return (
|
||||
return (
|
||||
<Modal title="Turn on MDM" onExit={onCancel} className={baseClass}>
|
||||
<div>
|
||||
<p className={`${baseClass}__description`}>
|
||||
To turn on MDM, Apple Inc. requires that you download and install a
|
||||
|
|
@ -119,12 +119,6 @@ const ManualEnrollMdmModal = ({
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal title="Turn on MDM" onExit={onCancel} className={baseClass}>
|
||||
{renderModalContent()}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,4 @@
|
|||
.manual-enroll-mdm-modal {
|
||||
width: 800px;
|
||||
|
||||
&__description {
|
||||
margin: $pad-large 0;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: $pad-large;
|
||||
list-style: number inside;
|
||||
}
|
||||
|
||||
&__download-button {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,23 @@
|
|||
// TODO: talk to rachel about removing this
|
||||
background-color: $ui-off-white;
|
||||
}
|
||||
.enroll-mdm-modal {
|
||||
width: 800px;
|
||||
|
||||
&__description {
|
||||
margin: $pad-large 0;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: $pad-large;
|
||||
list-style: number inside;
|
||||
}
|
||||
}
|
||||
|
||||
.device-user {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
5
tools/dbutils/delete-host
Executable file
5
tools/dbutils/delete-host
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
# experimental – doesn't always work right
|
||||
|
||||
docker compose exec mysql mysql -uroot -ptoor -Dfleet -e "DELETE FROM host_device_auth WHERE host_id=$1;"
|
||||
3
tools/dbutils/get-host-tokens
Executable file
3
tools/dbutils/get-host-tokens
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker compose exec mysql mysql -uroot -ptoor -Dfleet -e "SELECT host_id, token FROM host_device_auth;"
|
||||
7
tools/dbutils/insert-host-and-token
Executable file
7
tools/dbutils/insert-host-and-token
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
# experimental – doesn't always work right
|
||||
|
||||
NEW_ID=$1
|
||||
NEW_TOKEN=$2
|
||||
docker compose exec mysql mysql -uroot -ptoor -Dfleet -e "INSERT INTO host_device_auth VALUES ($NEW_ID, $NEW_TOKEN, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"
|
||||
43
tools/mdm/apple/toggle-mdm-dev.sh
Executable file
43
tools/mdm/apple/toggle-mdm-dev.sh
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
|
||||
# To toggle MDM, run `source toggle-mdm-dev`
|
||||
# Requires the env at $FLEET_ENV_PATH to contain logic something like:
|
||||
|
||||
# if [[ $USE_MDM == "1" ]]; then
|
||||
|
||||
# # for UI dev:
|
||||
# export FLEET_DEV_MDM_ENABLED=1
|
||||
|
||||
# # for MDM server
|
||||
# export FLEET_MDM_APPLE_ENABLE=1
|
||||
# export FLEET_MDM_APPLE_SCEP_CHALLENGE=scepchallenge
|
||||
# MDM_PATH={PATH_TO_YOUR_MDM_RELATED_KEYS_AND_CERTS}
|
||||
# export FLEET_MDM_APPLE_SCEP_CERT=$MDM_PATH"fleet-mdm-apple-scep.crt"
|
||||
# export FLEET_MDM_APPLE_SCEP_KEY=$MDM_PATH"fleet-mdm-apple-scep.key"
|
||||
# export FLEET_MDM_APPLE_BM_SERVER_TOKEN=$MDM_PATH"downloadtoken.p7m"
|
||||
# export FLEET_MDM_APPLE_BM_CERT=$MDM_PATH"fleet-apple-mdm-bm-public-key.crt"
|
||||
# export FLEET_MDM_APPLE_BM_KEY=$MDM_PATH"fleet-apple-mdm-bm-private.key"
|
||||
# #below files are from the shared Fleet 1Password
|
||||
# export FLEET_MDM_APPLE_APNS_CERT=$MDM_PATH"mdmcert.download.push.pem"
|
||||
# export FLEET_MDM_APPLE_APNS_KEY=$MDM_PATH"mdmcert.download.push.key"
|
||||
# else
|
||||
# unset FLEET_DEV_MDM_ENABLED
|
||||
# unset FLEET_MDM_APPLE_ENABLE
|
||||
# unset FLEET_MDM_APPLE_SCEP_CHALLENGE
|
||||
# unset FLEET_MDM_APPLE_SCEP_CERT
|
||||
# unset FLEET_MDM_APPLE_SCEP_KEY
|
||||
# unset FLEET_MDM_APPLE_BM_SERVER_TOKEN
|
||||
# unset FLEET_MDM_APPLE_BM_CERT
|
||||
# unset FLEET_MDM_APPLE_BM_KEY
|
||||
# #below files are from the shared Fleet 1Password
|
||||
# unset FLEET_MDM_APPLE_APNS_CERT
|
||||
# unset FLEET_MDM_APPLE_APNS_KEY
|
||||
# fi
|
||||
|
||||
if [[ $USE_MDM == "1" ]]; then
|
||||
export USE_MDM=0
|
||||
else
|
||||
export USE_MDM=1
|
||||
fi
|
||||
|
||||
source $FLEET_ENV_PATH
|
||||
Loading…
Reference in a new issue