diff --git a/changes/issue-11952-UI-for-windows-mdm-on-off b/changes/issue-11952-UI-for-windows-mdm-on-off new file mode 100644 index 0000000000..4a6eaa7f6e --- /dev/null +++ b/changes/issue-11952-UI-for-windows-mdm-on-off @@ -0,0 +1 @@ +- add ability to turn windows mdm on and off from the fleet UI diff --git a/frontend/__mocks__/configMock.ts b/frontend/__mocks__/configMock.ts index 2ebbd145c9..5feb718f0c 100644 --- a/frontend/__mocks__/configMock.ts +++ b/frontend/__mocks__/configMock.ts @@ -124,6 +124,7 @@ const DEFAULT_CONFIG_MOCK: IConfig = { }, fleet_desktop: { transparency_url: "https://fleetdm.com/transparency" }, mdm: { + windows_enabled_and_configured: true, apple_bm_default_team: "Apples", apple_bm_enabled_and_configured: true, apple_bm_terms_expired: false, diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts index a4ed1fe9bd..4272989c98 100644 --- a/frontend/interfaces/config.ts +++ b/frontend/interfaces/config.ts @@ -47,6 +47,7 @@ export default PropTypes.shape({ enabled_and_configured: PropTypes.bool, apple_bm_terms_expired: PropTypes.bool, apple_bm_enabled_and_configured: PropTypes.bool, + windows_enabled_and_configured: PropTypes.bool, macos_updates: PropTypes.shape({ minimum_version: PropTypes.string, deadline: PropTypes.string, @@ -116,6 +117,7 @@ export interface IMdmConfig { apple_bm_default_team?: string; apple_bm_terms_expired: boolean; apple_bm_enabled_and_configured: boolean; + windows_enabled_and_configured: boolean; end_user_authentication: IEndUserAuthentication; macos_updates: { minimum_version: string; @@ -282,6 +284,7 @@ export interface IConfig { }; }; mdm: IMdmConfig; + mdm_enabled?: boolean; // TODO: remove when windows MDM is released. Only used for windows MDM dev currently. } export interface IWebhookSettings { diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx index 24cdc05d6f..faba86e35b 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx @@ -8,14 +8,16 @@ import { AppContext } from "context/app"; import mdmAppleAPI from "services/entities/mdm_apple"; import { IMdmApple } from "interfaces/mdm"; +import { readableDate } from "utilities/helpers"; +import PATHS from "router/paths"; + import Button from "components/buttons/Button"; import CustomLink from "components/CustomLink"; import Spinner from "components/Spinner"; import DataError from "components/DataError"; -import { readableDate } from "utilities/helpers"; - import RequestCSRModal from "./components/RequestCSRModal"; import EndUserMigrationSection from "./components/EndUserMigrationSection/EndUserMigrationSection"; +import WindowsMdmSection from "./components/WindowsMdmSection/WindowsMdmSection"; const baseClass = "mdm-settings"; @@ -46,6 +48,10 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => { setShowRequestCSRModal(!showRequestCSRModal); }; + const navigateToWindowsMdm = () => { + router.push(PATHS.ADMIN_INTEGRATIONS_MDM_WINDOWS); + }; + // The API returns a 404 error if APNs is not configured yet, in that case we // want to prompt the user to download the certs and keys to configure the // server instead of the default error message. @@ -127,10 +133,19 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => {

Apple Push Certificates Portal

{isLoadingMdmApple ? : renderMdmAppleSection()} + {/* TODO: remove conditional rendering when windows MDM is released. */} + {config?.mdm_enabled && ( + + )} {isPremiumTier && ( -
- -
+ <> +
+ +
+ )} {showRequestCSRModal && ( diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx new file mode 100644 index 0000000000..be2e6861b6 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx @@ -0,0 +1,130 @@ +import React, { useContext } from "react"; +import { InjectedRouter } from "react-router"; + +import PATHS from "router/paths"; +import configAPI from "services/entities/config"; +import { NotificationContext } from "context/notification"; +import { AppContext } from "context/app"; + +import MainContent from "components/MainContent/MainContent"; +import Button from "components/buttons/Button"; +import BackLink from "components/BackLink/BackLink"; + +const baseClass = "windows-mdm-page"; + +interface ISetWindowsMdmOptions { + enable: boolean; + successMessage: string; + errorMessage: string; + router: InjectedRouter; +} + +const useSetWindowsMdm = ({ + enable, + successMessage, + errorMessage, + router, +}: ISetWindowsMdmOptions) => { + const { setConfig } = useContext(AppContext); + const { renderFlash } = useContext(NotificationContext); + + const turnOnWindowsMdm = async () => { + try { + const updatedConfig = await configAPI.update({ + mdm: { + windows_enabled_and_configured: enable, + }, + }); + setConfig(updatedConfig); + renderFlash("success", successMessage); + } catch { + renderFlash("error", errorMessage); + } finally { + router.push(PATHS.ADMIN_INTEGRATIONS_MDM); + } + }; + + return turnOnWindowsMdm; +}; + +interface IWindowsMdmOnContentProps { + router: InjectedRouter; +} + +const WindowsMdmOnContent = ({ router }: IWindowsMdmOnContentProps) => { + const turnOnWindowsMdm = useSetWindowsMdm({ + enable: true, + successMessage: "Windows MDM turned on (servers excluded).", + errorMessage: "Unable to turn on Windows MDM. Please try again.", + router, + }); + + return ( + <> +

Turn on Windows MDM

+

+ This will turn MDM on for Windows hosts with fleetd, overriding existing + MDM solutions. +

+

MDM won't be turned on for Windows servers

+ + + ); +}; + +interface IWindowsMdmOffContentProps { + router: InjectedRouter; +} + +const WindowsMdmOffContent = ({ router }: IWindowsMdmOffContentProps) => { + const turnOffWindowsMdm = useSetWindowsMdm({ + enable: false, + successMessage: "Windows MDM turned off.", + errorMessage: "Unable to turn off Windows MDM. Please try again.", + router, + }); + + return ( + <> +

Turn off Windows MDM

+

This will turn off MDM on each Windows host.

+ + + ); +}; + +interface IWindowsMdmPageProps { + router: InjectedRouter; +} + +const WindowsMdmPage = ({ router }: IWindowsMdmPageProps) => { + const { config } = useContext(AppContext); + + // TODO: remove when windows MDM is fully released. This is a temporary redirect + // when the feature is not enabled. + if (!config?.mdm_enabled) { + router.replace(PATHS.ADMIN_INTEGRATIONS_MDM); + } + + const isWindowsMdmEnabled = + config?.mdm?.windows_enabled_and_configured ?? false; + + return ( + + <> + + {isWindowsMdmEnabled ? ( + + ) : ( + + )} + + + ); +}; + +export default WindowsMdmPage; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss new file mode 100644 index 0000000000..840c887eeb --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/__styles.scss @@ -0,0 +1,15 @@ +.windows-mdm-page { + + &__back-to-mdm { + margin-bottom: $pad-xlarge; + } + + h1 { + margin-bottom: $pad-xxlarge; + } + + p { + font-size: $x-small; + margin: 0 0 $pad-large; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts new file mode 100644 index 0000000000..b26c35a468 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/index.ts @@ -0,0 +1 @@ +export { default } from "./WindowsMdmPage"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx new file mode 100644 index 0000000000..cf4f2da0ad --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/WindowsMdmSection.tsx @@ -0,0 +1,70 @@ +import React, { useContext } from "react"; + +import { AppContext } from "context/app"; + +import Card from "components/Card/Card"; +import Button from "components/buttons/Button"; +import Icon from "components/Icon"; + +const baseClass = "windows-mdm-section"; + +interface ITurnOnWindowsMdmProps { + onClickTurnOn: () => void; +} +const TurnOnWindowsMdm = ({ onClickTurnOn }: ITurnOnWindowsMdmProps) => { + return ( +
+
+

Turn on Windows MDM

+

Turn MDM on for Windows hosts with fleetd.

+
+ +
+ ); +}; + +interface ITurnOffWindowsMdmProps { + onClickEdit: () => void; +} + +const TurnOffWindowsMdm = ({ onClickEdit }: ITurnOffWindowsMdmProps) => { + return ( +
+
+ +

Windows MDM turned on (servers excluded).

+
+ +
+ ); +}; + +interface IWindowsMdmSectionProps { + turnOnWindowsMdm: () => void; + editWindowsMdm: () => void; +} + +const WindowsMdmSection = ({ + turnOnWindowsMdm, + editWindowsMdm, +}: IWindowsMdmSectionProps) => { + const { config } = useContext(AppContext); + + const isWindowsMdmEnabled = + config?.mdm?.windows_enabled_and_configured ?? false; + + return ( + + {isWindowsMdmEnabled ? ( + + ) : ( + + )} + + ); +}; + +export default WindowsMdmSection; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss new file mode 100644 index 0000000000..3ed546487a --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/_styles.scss @@ -0,0 +1,33 @@ +.windows-mdm-section { + font-size: $x-small; + + p { + margin: 0; + } + + &__turn-on-windows, &__turn-off-windows { + display: flex; + justify-content: space-between; + align-items: center; + } + + &__turn-on-windows { + h3 { + font-size: $x-small; + font-weight: $bold; + margin: 0 0 $pad-xsmall; + } + } + + &__turn-off-windows { + > div { + display: flex; + align-items: center; + } + + p { + margin-left: $pad-small; + } + } + +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts new file mode 100644 index 0000000000..2e04c3a5cc --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/WindowsMdmSection/index.ts @@ -0,0 +1 @@ +export { default } from "./WindowsMdmSection"; diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx index eed5d3b896..69909599bd 100644 --- a/frontend/router/index.tsx +++ b/frontend/router/index.tsx @@ -53,6 +53,7 @@ import AgentOptionsPage from "pages/admin/TeamManagementPage/TeamDetailsWrapper/ import MacOSUpdates from "pages/ManageControlsPage/MacOSUpdates"; import MacOSSettings from "pages/ManageControlsPage/MacOSSettings"; import MacOSSetup from "pages/ManageControlsPage/MacOSSetup/MacOSSetup"; +import WindowsMdmPage from "pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage"; import PATHS from "router/paths"; @@ -136,6 +137,7 @@ const routes = ( + diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts index 88e688c7c9..b6230e9f98 100644 --- a/frontend/router/paths.ts +++ b/frontend/router/paths.ts @@ -22,6 +22,7 @@ export default { ADMIN_INTEGRATIONS: `${URL_PREFIX}/settings/integrations`, ADMIN_INTEGRATIONS_TICKET_DESTINATIONS: `${URL_PREFIX}/settings/integrations/ticket-destinations`, ADMIN_INTEGRATIONS_MDM: `${URL_PREFIX}/settings/integrations/mdm`, + ADMIN_INTEGRATIONS_MDM_WINDOWS: `${URL_PREFIX}/settings/integrations/mdm/windows`, ADMIN_INTEGRATIONS_AUTOMATIC_ENROLLMENT: `${URL_PREFIX}/settings/integrations/automatic-enrollment`, ADMIN_TEAMS: `${URL_PREFIX}/settings/teams`, ADMIN_SETTINGS: `${URL_PREFIX}/settings`,