handle all three ios and ipad unenrollment states and diplay correct copy in unenroll modal (#33804)

This is a quick fix to handle all three cases when showing the unenroll
modal for ios and ipad devices.
It also adds better naming to the functions that check the enrollment
type.

- [x] QA'd all new/changed functionality manually
This commit is contained in:
Gabriel Hernandez
2025-10-06 10:44:58 +01:00
committed by GitHub
parent 5f98be0f08
commit 83a637469c
7 changed files with 88 additions and 42 deletions
+25 -6
View File
@@ -49,6 +49,7 @@ export type MdmEnrollmentStatus =
| "On (manual)"
| "On (automatic)"
| "On (personal)"
| "On (company-owned)"
| "Off"
| "Pending";
@@ -94,6 +95,10 @@ export const MDM_ENROLLMENT_STATUS_UI_MAP: Record<
displayName: "Pending",
filterValue: "pending",
},
"On (company-owned)": {
displayName: "On (company-owned)",
filterValue: "automatic",
},
};
export interface IMdmStatusCardData {
@@ -261,7 +266,7 @@ export interface IMdmCommandResult {
host_uuid: string;
command_uuid: string;
/** Status is the status of the command. It can be one of Acknowledged, Error, or NotNow for
// Apple, or 200, 400, etc for Windows. */
// Apple, or 200, 400, etc for Windows. */
status: string;
updated_at: string;
request_type: string;
@@ -278,14 +283,28 @@ export const isEnrolledInMdm = (
if (!hostMdmEnrollmentStatus) {
return false;
}
return ["On (automatic)", "On (manual)", "On (personal)"].includes(
hostMdmEnrollmentStatus
);
return [
"On (automatic)",
"On (manual)",
"On (personal)",
"On (company-owned)",
].includes(hostMdmEnrollmentStatus);
};
/** determines if the host enrolled in mdm is a personal device */
export const isPersonalEnrollmentInMdm = (
export const isBYODManualEnrollment = (
enrollmentStatus: MdmEnrollmentStatus | null
) => {
return enrollmentStatus === "On (manual)";
};
export const isBYODAccountDrivenEnrollment = (
enrollmentStatus: MdmEnrollmentStatus | null
) => {
return enrollmentStatus === "On (personal)";
};
export const isCompanyOwnedEnrollment = (
enrollmentStatus: MdmEnrollmentStatus | null
) => {
return enrollmentStatus === "On (company-owned)";
};
@@ -11,7 +11,7 @@ import {
isAppleDevice,
isMobilePlatform,
} from "interfaces/platform";
import { isPersonalEnrollmentInMdm } from "interfaces/mdm";
import { isBYODAccountDrivenEnrollment } from "interfaces/mdm";
import TooltipWrapperArchLinuxRolling from "components/TooltipWrapperArchLinuxRolling";
import Checkbox from "components/forms/fields/Checkbox";
@@ -644,7 +644,9 @@ const allHostTableHeaders: IHostTableColumnConfig[] = [
// TODO(android): is iOS/iPadOS supported?
if (
isAndroid(cellProps.row.original.platform) ||
isPersonalEnrollmentInMdm(cellProps.row.original.mdm.enrollment_status)
isBYODAccountDrivenEnrollment(
cellProps.row.original.mdm.enrollment_status
)
) {
return NotSupported;
}
@@ -10,7 +10,10 @@ import {
isIPadOrIPhone,
} from "interfaces/platform";
import { isScriptSupportedPlatform } from "interfaces/script";
import { isPersonalEnrollmentInMdm, MdmEnrollmentStatus } from "interfaces/mdm";
import {
isBYODAccountDrivenEnrollment,
MdmEnrollmentStatus,
} from "interfaces/mdm";
import {
HostMdmDeviceStatusUIState,
@@ -177,16 +180,16 @@ const canWipeHost = ({
const canWipeWindowsOrAppleOS =
hostMdmEnabled && isConnectedToFleetMdm && isEnrolledInMdm;
// there is a special case for iOS and iPadOS devices that are personally enrolled
// there is a special case for iOS and iPadOS devices that are account driven enrolled
// in MDM. These hosts cannot be wiped.
const isPersonallyEnrolledIosOrIpadDevice =
const isAccountDrivenEnrolledIosOrIpadosDevice =
isIPadOrIPhone(hostPlatform) &&
isPersonalEnrollmentInMdm(hostMdmEnrollmentStatus);
isBYODAccountDrivenEnrollment(hostMdmEnrollmentStatus);
return (
isPremiumTier &&
!isAndroid(hostPlatform) &&
!isPersonallyEnrolledIosOrIpadDevice &&
!isAccountDrivenEnrolledIosOrIpadosDevice &&
hostMdmDeviceStatus === "unlocked" &&
(isLinuxLike(hostPlatform) || canWipeWindowsOrAppleOS) &&
(isGlobalAdmin || isGlobalMaintainer || isTeamAdmin || isTeamMaintainer)
@@ -42,6 +42,7 @@ import {
IHostCertificate,
CERTIFICATES_DEFAULT_SORT,
} from "interfaces/certificates";
import { isBYODAccountDrivenEnrollment } from "interfaces/mdm";
import { normalizeEmptyValues, wrapFleetHelper } from "utilities/helpers";
import permissions from "utilities/permissions";
@@ -59,7 +60,6 @@ import {
isIPadOrIPhone,
isLinuxLike,
} from "interfaces/platform";
import { isPersonalEnrollmentInMdm } from "interfaces/mdm";
import Spinner from "components/Spinner";
import TabNav from "components/TabNav";
@@ -1021,10 +1021,10 @@ const HostDetailsPage = ({
)}
</TabPanel>
<TabPanel>
{/* There is a special case for personally enrolled mdm hosts where we are not
{/* There is a special case for BYOD account driven enrolled mdm hosts where we are not
currently supporting software installs. This check should be removed
when we add that feature. */}
{isPersonalEnrollmentInMdm(host.mdm.enrollment_status) ? (
{isBYODAccountDrivenEnrollment(host.mdm.enrollment_status) ? (
<EmptyTable
header="Software library is currently not supported on this host."
info={
@@ -1351,14 +1351,12 @@ const HostDetailsPage = ({
onProfileResent={refetchHostDetails}
/>
)}
{showUnenrollMdmModal && !!host && (
{showUnenrollMdmModal && !!host && host.mdm.enrollment_status && (
<UnenrollMdmModal
hostId={host.id}
hostPlatform={host.platform}
hostName={host.display_name}
isBYODEnrollment={isPersonalEnrollmentInMdm(
host.mdm.enrollment_status
)}
enrollmentStatus={host.mdm.enrollment_status}
onClose={toggleUnenrollMdmModal}
/>
)}
@@ -4,25 +4,32 @@ import DataError from "components/DataError";
import Button from "components/buttons/Button";
import Modal from "components/Modal";
import { NotificationContext } from "context/notification";
import CustomLink from "components/CustomLink";
import mdmAPI from "services/entities/mdm";
import { isAndroid, isIPadOrIPhone } from "interfaces/platform";
import {
isBYODAccountDrivenEnrollment,
isBYODManualEnrollment,
isCompanyOwnedEnrollment,
MdmEnrollmentStatus,
} from "interfaces/mdm";
const baseClass = "unenroll-mdm-modal";
interface IUnenrollMdmModalProps {
hostId: number;
hostPlatform: string;
hostName: string;
isBYODEnrollment?: boolean;
enrollmentStatus: MdmEnrollmentStatus | null;
onClose: () => void;
}
const baseClass = "unenroll-mdm-modal";
const UnenrollMdmModal = ({
hostId,
hostPlatform,
hostName,
isBYODEnrollment = false,
enrollmentStatus,
onClose,
}: IUnenrollMdmModalProps) => {
const [requestState, setRequestState] = useState<
@@ -66,27 +73,43 @@ const UnenrollMdmModal = ({
setRequestState(undefined);
};
const generateIosOrIpadosDescription = () => {
if (isBYODManualEnrollment(enrollmentStatus)) {
return (
<p>
To re-enroll, go to <b>Hosts &gt; Add hosts &gt; iOS/iPadOS</b> and
share the link with end user.
</p>
);
} else if (isBYODAccountDrivenEnrollment(enrollmentStatus)) {
return (
<p>
To re-enroll, ask your end user to navigate to{" "}
<b>
Settings &gt; General &gt; VPN &amp; Device Management &gt; Sign in
to Work or School Account...
</b>{" "}
on their host and to log in with their work email.
</p>
);
} else if (isCompanyOwnedEnrollment(enrollmentStatus)) {
return (
<p>
To re-enroll, make sure that the host is still in Apple Business
Manager (ABM). The host will automatically enroll after it&apos;s
reset.
</p>
);
}
return null;
};
const generateDescription = () => {
if (isIPadOrIPhone(hostPlatform)) {
return (
<>
<p>Settings configured by Fleet will be removed.</p>
{isBYODEnrollment ? (
<p>
To re-enroll, ask your end user to navigate to{" "}
<b>
Settings &gt; General &gt; VPN &amp; Device Management &gt; Sign
in to Work or School Account...
</b>{" "}
on their host and to log in with their work email.
</p>
) : (
<p>
To re-enroll, make sure that the host is still in Apple Business
Manager (ABM). The host will automatically enroll after it&apos;s
reset.
</p>
)}
{generateIosOrIpadosDescription()}
</>
);
}
@@ -4,7 +4,7 @@ import classnames from "classnames";
import { IHostMdmData, IMunkiData } from "interfaces/host";
import { isAndroid, isIPadOrIPhone } from "interfaces/platform";
import {
isPersonalEnrollmentInMdm,
isBYODAccountDrivenEnrollment,
MDM_ENROLLMENT_STATUS_UI_MAP,
} from "interfaces/mdm";
import {
@@ -55,7 +55,7 @@ const About = ({ aboutData, munki, mdm, className }: IAboutProps) => {
// for all host types, we show the Enrollment ID dataset if the host
// is enrolled in MDM personally. Personal (BYOD) devices do not report
// their serial numbers, so we show the Enrollment ID instead.
if (mdm && isPersonalEnrollmentInMdm(mdm.enrollment_status)) {
if (mdm && isBYODAccountDrivenEnrollment(mdm.enrollment_status)) {
deviceIdDataSet = (
<DataSet
title={
+1
View File
@@ -362,6 +362,7 @@ export const MDM_STATUS_TOOLTIP: Record<
or by signing in with Google account. End user can turn MDM off.
</span>
),
"On (company-owned)": null,
Off: undefined, // no tooltip specified
Pending: (
<span>