Add /enroll URL for macOS in Add hosts modal (#47528)
**Related issue:** Resolves #38874 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added macOS enrollment details in the “Add hosts” flow, including a clearer choice between **Personal (BYOD)** and **Company-owned** devices. * Shows a copyable macOS enrollment URL when MDM is configured, updating the URL based on the selected device type. * Keeps the macOS setup experience aligned with the enrollment method, including packaging guidance when MDM isn’t enabled. * **Tests** * Added coverage for macOS enrollment URL rendering and device-type switching in the “Add hosts” modal. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Added enrollment profile URL to the macOS tab in the "Add hosts" modal, with enrollment type selection (company-owned or personal/BYOD) for MDM users.
|
||||
@@ -94,6 +94,48 @@ describe("AddHostsModal", () => {
|
||||
expect(screen.queryByText(/--enable-scripts/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders enroll url input for macOS if mac mdm is enabled", async () => {
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
context: {
|
||||
app: {
|
||||
isMacMdmEnabledAndConfigured: true,
|
||||
isPreviewMode: false,
|
||||
config: createMockConfig(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { user } = render(
|
||||
<AddHostsModal
|
||||
isAnyTeamSelected
|
||||
enrollSecret={ENROLL_SECRET}
|
||||
isLoading={false}
|
||||
onCancel={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("tab", { name: "macOS" }));
|
||||
expect(screen.getByLabelText("Personal (BYOD)")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Company-owned")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Send this to your end users:/i)
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Company-owned is selected by default — URL has no byod param
|
||||
const urlInput = screen.getByDisplayValue(
|
||||
new RegExp(`/enroll\\?enroll_secret=${ENROLL_SECRET}$`)
|
||||
);
|
||||
expect(urlInput).toBeInTheDocument();
|
||||
|
||||
// Switching to Personal (BYOD) appends byod=true
|
||||
await user.click(screen.getByLabelText("Personal (BYOD)"));
|
||||
const byodUrlInput = screen.getByDisplayValue(
|
||||
new RegExp(`/enroll\\?enroll_secret=${ENROLL_SECRET}&byod=true`)
|
||||
);
|
||||
expect(byodUrlInput).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders enroll url input for ios & ipadOS if mac mdm is enabled", async () => {
|
||||
const render = createCustomRenderer({
|
||||
withBackendMock: true,
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
|
||||
import { AppContext } from "context/app";
|
||||
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
|
||||
import { getPathWithQueryParams } from "utilities/url";
|
||||
import CustomLink from "components/CustomLink";
|
||||
import Radio from "components/forms/fields/Radio";
|
||||
import InputField from "components/forms/fields/InputField";
|
||||
|
||||
type DeviceType = "companyOwned" | "personalBYOD";
|
||||
|
||||
const generateInstallerString = (
|
||||
serverUrl: string,
|
||||
enrollSecret: string,
|
||||
scriptsDisabled: boolean
|
||||
) => {
|
||||
return `fleetctl package --type=pkg ${
|
||||
!scriptsDisabled ? "--enable-scripts " : ""
|
||||
}--fleet-desktop --fleet-url=${serverUrl} --enroll-secret=${enrollSecret}`;
|
||||
};
|
||||
|
||||
const baseClass = "macos-panel";
|
||||
|
||||
interface IMacosPanelProps {
|
||||
enrollSecret: string;
|
||||
}
|
||||
|
||||
const MacosPanel = ({ enrollSecret }: IMacosPanelProps) => {
|
||||
const { config, isMacMdmEnabledAndConfigured } = useContext(AppContext);
|
||||
|
||||
const [deviceType, setDeviceType] = useState<DeviceType>("companyOwned");
|
||||
|
||||
if (!config) return null;
|
||||
|
||||
if (isMacMdmEnabledAndConfigured) {
|
||||
const enrollUrl = getPathWithQueryParams(
|
||||
`${config.server_settings.server_url}/enroll`,
|
||||
{
|
||||
enroll_secret: enrollSecret,
|
||||
byod: deviceType === "personalBYOD" ? "true" : undefined,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<form>
|
||||
<fieldset className="form-field">
|
||||
<Radio
|
||||
label="Personal (BYOD)"
|
||||
id="personal-byod"
|
||||
checked={deviceType === "personalBYOD"}
|
||||
value="personalBYOD"
|
||||
name="device-type"
|
||||
onChange={() => setDeviceType("personalBYOD")}
|
||||
/>
|
||||
<Radio
|
||||
label="Company-owned"
|
||||
id="company-owned"
|
||||
checked={deviceType === "companyOwned"}
|
||||
value="companyOwned"
|
||||
name="device-type"
|
||||
onChange={() => setDeviceType("companyOwned")}
|
||||
/>
|
||||
</fieldset>
|
||||
<InputField
|
||||
readOnly
|
||||
inputWrapperClass={`${baseClass}__enroll-link`}
|
||||
name="enroll-link"
|
||||
enableCopy
|
||||
label="Send this to your end users:"
|
||||
value={enrollUrl}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const installerString = generateInstallerString(
|
||||
config.server_settings.server_url,
|
||||
enrollSecret,
|
||||
config.server_settings.scripts_disabled
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<InputField
|
||||
readOnly
|
||||
inputWrapperClass={`${baseClass}__installer-input`}
|
||||
name="installer"
|
||||
enableCopy
|
||||
label={
|
||||
<>
|
||||
Use this command to generate Fleet's agent.{" "}
|
||||
<CustomLink
|
||||
url={`${LEARN_MORE_ABOUT_BASE_LINK}/generate-fleets-agent`}
|
||||
text="Learn how"
|
||||
newTab
|
||||
/>
|
||||
</>
|
||||
}
|
||||
type="textarea"
|
||||
value={installerString}
|
||||
helpText="Run this on your computer, then deploy the generated package to your hosts."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MacosPanel;
|
||||
@@ -0,0 +1,6 @@
|
||||
.macos-panel {
|
||||
&__enroll-link input {
|
||||
font-family: "SourceCodePro", $monospace;
|
||||
color: $core-fleet-blue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MacosPanel";
|
||||
@@ -20,6 +20,7 @@ import TabText from "components/TabText";
|
||||
import { isValidPemCertificate } from "../../../pages/hosts/ManageHostsPage/helpers";
|
||||
import IosIpadosPanel from "./IosIpadosPanel";
|
||||
import AndroidPanel from "./AndroidPanel";
|
||||
import MacosPanel from "./MacosPanel";
|
||||
|
||||
interface IPlatformSubNav {
|
||||
name: string;
|
||||
@@ -281,9 +282,6 @@ const PlatformWrapper = ({
|
||||
hosts. For ARM, use <code>--arch=arm64</code>
|
||||
</>
|
||||
);
|
||||
} else if (packageType === "pkg") {
|
||||
packageTypeHelpText =
|
||||
"Run this on your computer, then deploy the generated package to your hosts.";
|
||||
} else {
|
||||
packageTypeHelpText = "";
|
||||
}
|
||||
@@ -347,6 +345,10 @@ const PlatformWrapper = ({
|
||||
return <AndroidPanel enrollSecret={enrollSecret} />;
|
||||
}
|
||||
|
||||
if (packageType === "pkg") {
|
||||
return <MacosPanel enrollSecret={enrollSecret} />;
|
||||
}
|
||||
|
||||
if (packageType === "advanced") {
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user