Fleet UI: iOS and iPadOS host actions dropdown (#19599)

This commit is contained in:
RachelElysia
2024-06-12 12:16:23 -04:00
committed by GitHub
parent bcec7c9fba
commit a88a25aa00
7 changed files with 82 additions and 31 deletions
+8
View File
@@ -68,6 +68,8 @@ export const HOST_LINUX_PLATFORMS = [
"tuxedo",
] as const;
export const HOST_APPLE_PLATFORMS = ["darwin", "ios", "ipados"] as const;
/**
* Checks if the provided platform is a Linux-like OS. We can recieve many
* different types of host platforms so we need a check that will cover all
@@ -78,3 +80,9 @@ export const isLinuxLike = (platform: string) => {
platform as typeof HOST_LINUX_PLATFORMS[number]
);
};
export const isAppleDevice = (platform: string) => {
return HOST_APPLE_PLATFORMS.includes(
platform as typeof HOST_APPLE_PLATFORMS[number]
);
};
+1 -1
View File
@@ -9,7 +9,7 @@ export interface IScript {
}
export const isScriptSupportedPlatform = (hostPlatform: string) =>
["darwin", "windows", ...HOST_LINUX_PLATFORMS].includes(hostPlatform); // excludes chrome, see also https://github.com/fleetdm/fleet/blob/5a21e2cfb029053ddad0508869eb9f1f23997bf2/server/fleet/hosts.go#L775
["darwin", "windows", ...HOST_LINUX_PLATFORMS].includes(hostPlatform); // excludes chrome, ios, ipados see also https://github.com/fleetdm/fleet/blob/5a21e2cfb029053ddad0508869eb9f1f23997bf2/server/fleet/hosts.go#L775
export type IScriptExecutionStatus = "ran" | "pending" | "error";
@@ -785,7 +785,7 @@ describe("Host Actions Dropdown", () => {
expect(screen.queryByText("Unlock")).not.toBeInTheDocument();
});
it("does not renders when a mac host but does not have Fleet mac mdm enabled and configured", async () => {
it("does not renders when a macOS host but does not have Fleet mac mdm enabled and configured", async () => {
const render = createCustomRenderer({
context: {
app: {
@@ -921,7 +921,7 @@ describe("Host Actions Dropdown", () => {
expect(screen.queryByText("Wipe")).not.toBeInTheDocument();
});
it("does not renders when a mac host but does not have Fleet mac mdm enabled and configured", async () => {
it("does not renders when a macOS host but does not have Fleet macOS mdm enabled and configured", async () => {
const render = createCustomRenderer({
context: {
app: {
@@ -1139,55 +1139,85 @@ describe("Host Actions Dropdown", () => {
});
});
describe("Does not render dropdown for certain platforms", () => {
it("does not render dropdown for iOS", async () => {
describe("Render options only available for iOS and iPadOS", () => {
it("renders only the transfer, wipe, and delete options for iOS", async () => {
const render = createCustomRenderer({
context: {
app: {
isPremiumTier: true,
isGlobalAdmin: true,
isMacMdmEnabledAndConfigured: true,
currentUser: createMockUser(),
},
},
});
render(
const { user } = render(
<HostActionsDropdown
hostTeamId={null}
onSelect={noop}
hostStatus="online"
hostPlatform="ios"
hostMdmEnrollmentStatus={null}
hostMdmEnrollmentStatus="On (automatic)"
mdmName="Fleet"
hostMdmDeviceStatus={"unlocked"}
hostScriptsEnabled={false}
/>
);
expect(screen.queryByText("Actions")).not.toBeInTheDocument();
await user.click(screen.getByText("Actions"));
expect(screen.queryByText("Transfer")).toBeInTheDocument();
expect(screen.queryByText("Wipe")).toBeInTheDocument();
expect(screen.queryByText("Delete")).toBeInTheDocument();
expect(screen.queryByText("Query")).not.toBeInTheDocument();
expect(screen.queryByText("Run script")).not.toBeInTheDocument();
expect(
screen.queryByText("Show disk encryption key")
).not.toBeInTheDocument();
expect(screen.queryByText("Turn off MDM")).not.toBeInTheDocument();
expect(screen.queryByText("Lock")).not.toBeInTheDocument();
});
it("does not render dropdown for iPadOS", async () => {
it("renders only the transfer, wipe, and delete options for iPadOS", async () => {
const render = createCustomRenderer({
context: {
app: {
isPremiumTier: true,
isGlobalAdmin: true,
isMacMdmEnabledAndConfigured: true,
currentUser: createMockUser(),
},
},
});
render(
const { user } = render(
<HostActionsDropdown
hostTeamId={null}
onSelect={noop}
hostStatus="online"
hostPlatform="ipados"
hostMdmEnrollmentStatus={null}
hostMdmEnrollmentStatus="On (automatic)"
mdmName="Fleet"
hostMdmDeviceStatus={"unlocked"}
hostScriptsEnabled={false}
/>
);
expect(screen.queryByText("Actions")).not.toBeInTheDocument();
await user.click(screen.getByText("Actions"));
expect(screen.queryByText("Transfer")).toBeInTheDocument();
expect(screen.queryByText("Wipe")).toBeInTheDocument();
expect(screen.queryByText("Delete")).toBeInTheDocument();
expect(screen.queryByText("Query")).not.toBeInTheDocument();
expect(screen.queryByText("Run script")).not.toBeInTheDocument();
expect(
screen.queryByText("Show disk encryption key")
).not.toBeInTheDocument();
expect(screen.queryByText("Turn off MDM")).not.toBeInTheDocument();
expect(screen.queryByText("Lock")).not.toBeInTheDocument();
});
});
});
@@ -79,10 +79,6 @@ const HostActionsDropdown = ({
// No options to render. Exit early
if (options.length === 0) return null;
if (hostPlatform === "ios" || hostPlatform === "ipados") {
return null;
}
return (
<div className={baseClass}>
<Dropdown
@@ -2,7 +2,7 @@ import React from "react";
import { cloneDeep } from "lodash";
import { IDropdownOption } from "interfaces/dropdownOption";
import { isLinuxLike } from "interfaces/platform";
import { isLinuxLike, isAppleDevice } from "interfaces/platform";
import { isScriptSupportedPlatform } from "interfaces/script";
import {
@@ -86,6 +86,7 @@ const canTransferTeam = (config: IHostActionConfigOptions) => {
const canEditMdm = (config: IHostActionConfigOptions) => {
const {
hostPlatform,
isGlobalAdmin,
isGlobalMaintainer,
isTeamAdmin,
@@ -95,7 +96,7 @@ const canEditMdm = (config: IHostActionConfigOptions) => {
isMacMdmEnabledAndConfigured,
} = config;
return (
config.hostPlatform === "darwin" &&
hostPlatform === "darwin" &&
isMacMdmEnabledAndConfigured &&
isEnrolledInMdm &&
isFleetMdm &&
@@ -103,6 +104,13 @@ const canEditMdm = (config: IHostActionConfigOptions) => {
);
};
const canQueryHost = ({ hostPlatform }: IHostActionConfigOptions) => {
// Currently we cannot query iOS or iPadOS
const isIosOrIpadosHost = hostPlatform === "ios" || hostPlatform === "ipados";
return !isIosOrIpadosHost;
};
const canLockHost = ({
isPremiumTier,
hostPlatform,
@@ -146,17 +154,18 @@ const canWipeHost = ({
hostMdmDeviceStatus,
}: IHostActionConfigOptions) => {
const hostMdmEnabled =
(hostPlatform === "darwin" && isMacMdmEnabledAndConfigured) ||
(isAppleDevice(hostPlatform) && isMacMdmEnabledAndConfigured) ||
(hostPlatform === "windows" && isWindowsMdmEnabledAndConfigured);
// macOS and Windows hosts have the same conditions and can be wiped if they
// Windows and Apple devices (i.e. macOS, iOS, iPadOS) have the same conditions and can be wiped if they
// are enrolled in MDM and the MDM is enabled.
const canWipeMacOrWindows = hostMdmEnabled && isFleetMdm && isEnrolledInMdm;
const canWipeWindowsOrAppleOS =
hostMdmEnabled && isFleetMdm && isEnrolledInMdm;
return (
isPremiumTier &&
hostMdmDeviceStatus === "unlocked" &&
(isLinuxLike(hostPlatform) || canWipeMacOrWindows) &&
(isLinuxLike(hostPlatform) || canWipeWindowsOrAppleOS) &&
(isGlobalAdmin || isGlobalMaintainer || isTeamAdmin || isTeamMaintainer)
);
};
@@ -205,8 +214,12 @@ const canDeleteHost = (config: IHostActionConfigOptions) => {
};
const canShowDiskEncryption = (config: IHostActionConfigOptions) => {
const { isPremiumTier, doesStoreEncryptionKey } = config;
return isPremiumTier && doesStoreEncryptionKey;
const { isPremiumTier, doesStoreEncryptionKey, hostPlatform } = config;
// Currently we cannot show disk encryption key for iOS or iPadOS
const isIosOrIpadosHost = hostPlatform === "ios" || hostPlatform === "ipados";
return isPremiumTier && doesStoreEncryptionKey && !isIosOrIpadosHost;
};
const canRunScript = ({
@@ -237,6 +250,10 @@ const removeUnavailableOptions = (
options = options.filter((option) => option.value !== "transfer");
}
if (!canQueryHost(config)) {
options = options.filter((option) => option.value !== "query");
}
if (!canShowDiskEncryption(config)) {
options = options.filter((option) => option.value !== "diskEncryption");
}
@@ -266,9 +283,8 @@ const removeUnavailableOptions = (
}
// TODO: refactor to filter in one pass using predefined filters specified for each of the
// DEFAULT_OPTIONS. Note that as currently, structured the default is to include all options. For
// example, "Query" is implicitly included by default because there is no equivalent `canQuery`
// filter being applied here. This is a bit confusing since
// DEFAULT_OPTIONS. Note that as currently, structured the default is to include all options.
// This is a bit confusing since we remove options instead of add options
return options;
};
@@ -77,7 +77,7 @@ import TransferHostModal from "../../components/TransferHostModal";
import DeleteHostModal from "../../components/DeleteHostModal";
import DiskEncryptionKeyModal from "./modals/DiskEncryptionKeyModal";
import HostActionDropdown from "./HostActionsDropdown/HostActionsDropdown";
import HostActionsDropdown from "./HostActionsDropdown/HostActionsDropdown";
import OSSettingsModal from "../OSSettingsModal";
import BootstrapPackageModal from "./modals/BootstrapPackageModal";
import RunScriptModal from "./modals/RunScriptModal";
@@ -672,7 +672,7 @@ const HostDetailsPage = ({
}
return (
<HostActionDropdown
<HostActionsDropdown
hostTeamId={host.team_id}
onSelect={onSelectHostAction}
hostPlatform={host.platform}
@@ -1,4 +1,5 @@
import React from "react";
import { isAppleDevice } from "interfaces/platform";
import { HostMdmDeviceStatusUIState } from "../../helpers";
interface IDeviceStatusTag {
@@ -23,7 +24,7 @@ export const DEVICE_STATUS_TAGS: DeviceStatusTagConfig = {
title: "LOCKED",
tagType: "warning",
generateTooltip: (platform) =>
platform === "darwin"
isAppleDevice(platform)
? "Host is locked. The end user cant use the host until the six-digit PIN has been entered."
: "Host is locked. The end user cant use the host until the host has been unlocked.",
},
@@ -43,7 +44,7 @@ export const DEVICE_STATUS_TAGS: DeviceStatusTagConfig = {
title: "WIPED",
tagType: "error",
generateTooltip: (platform) =>
platform === "darwin"
isAppleDevice(platform)
? "Host is wiped. To prevent the host from automatically reenrolling to Fleet, first release the host from Apple Business Manager and then delete the host in Fleet."
: "Host is wiped.",
},