diff --git a/frontend/__mocks__/configMock.ts b/frontend/__mocks__/configMock.ts index fb7c6d9926..3d95dc4492 100644 --- a/frontend/__mocks__/configMock.ts +++ b/frontend/__mocks__/configMock.ts @@ -3,6 +3,7 @@ import { IConfig, ILicense, IMdmConfig } from "interfaces/config"; const DEFAULT_CONFIG_MDM_MOCK: IMdmConfig = { apple_server_url: "", enable_disk_encryption: false, + windows_require_bitlocker_pin: false, windows_enabled_and_configured: true, apple_bm_default_team: "Apples", apple_bm_enabled_and_configured: true, diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts index 502eb660e5..bef8b5e17e 100644 --- a/frontend/interfaces/config.ts +++ b/frontend/interfaces/config.ts @@ -47,6 +47,7 @@ export interface IMdmConfig { /** Update this URL if you're self-hosting Fleet and you want your hosts to talk to a different URL for MDM features. (If not configured, hosts will use the base URL of the Fleet instance.) */ apple_server_url: string; enable_disk_encryption: boolean; + windows_require_bitlocker_pin: boolean; /** `enabled_and_configured` only tells us if Apples MDM has been enabled and configured correctly. The naming is slightly confusing but at one point we only supported apple mdm, so thats why it's name the way it is. */ diff --git a/frontend/interfaces/team.ts b/frontend/interfaces/team.ts index 8f5bdc8bc0..541bfbcfbf 100644 --- a/frontend/interfaces/team.ts +++ b/frontend/interfaces/team.ts @@ -49,6 +49,7 @@ export interface ITeam extends ITeamSummary { role?: UserRole; // role value is included when the team is in the context of a user mdm?: { enable_disk_encryption: boolean; + windows_require_bitlocker_pin: boolean; macos_updates: IAppleDeviceUpdates; ios_updates: IAppleDeviceUpdates; ipados_updates: IAppleDeviceUpdates; diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/DiskEncryption.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/DiskEncryption.tsx index a128b377f9..7f6b4374b5 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/DiskEncryption.tsx +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/DiskEncryption.tsx @@ -21,6 +21,7 @@ import Spinner from "components/Spinner"; import SectionHeader from "components/SectionHeader"; import TooltipWrapper from "components/TooltipWrapper"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; +import RevealButton from "components/buttons/RevealButton"; import DiskEncryptionTable from "./components/DiskEncryptionTable"; @@ -43,12 +44,20 @@ const DiskEncryption = ({ ? false : config?.mdm.enable_disk_encryption ?? false; + const defaultRequireBitLockerPIN = currentTeamId + ? false + : config?.mdm.windows_require_bitlocker_pin ?? false; + const [isLoadingTeam, setIsLoadingTeam] = useState(true); const [showAggregate, setShowAggregate] = useState(defaultShowDiskEncryption); const [diskEncryptionEnabled, setDiskEncryptionEnabled] = useState( defaultShowDiskEncryption ); + const [requireBitLockerPIN, setRequireBitLockerPIN] = useState( + defaultRequireBitLockerPIN + ); + const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); // because we pull the default state for no teams from the config, // we need to update the config when the user toggles the checkbox @@ -64,10 +73,14 @@ const DiskEncryption = ({ } }; - const onToggleCheckbox = (value: boolean) => { + const onToggleDiskEncryption = (value: boolean) => { setDiskEncryptionEnabled(value); }; + const onToggleRequireBitLockerPIN = (value: boolean) => { + setRequireBitLockerPIN(value); + }; + useQuery( ["team", currentTeamId], () => teamsAPI.load(currentTeamId), @@ -79,6 +92,8 @@ const DiskEncryption = ({ onSuccess: (res) => { const enableDiskEncryption = res.mdm?.enable_disk_encryption ?? false; setDiskEncryptionEnabled(enableDiskEncryption); + const pinRequired = res.mdm?.windows_require_bitlocker_pin ?? false; + setRequireBitLockerPIN(pinRequired); setShowAggregate(enableDiskEncryption); setIsLoadingTeam(false); }, @@ -89,6 +104,7 @@ const DiskEncryption = ({ try { await diskEncryptionAPI.updateDiskEncryption( diskEncryptionEnabled, + requireBitLockerPIN, currentTeamId ); renderFlash( @@ -196,7 +212,7 @@ const DiskEncryption = ({ )} @@ -211,6 +227,44 @@ const DiskEncryption = ({ newTab />

+ {diskEncryptionEnabled && featureFlags.showBitLockerPINOption && ( +
+ setShowAdvancedOptions(!showAdvancedOptions)} + /> + {showAdvancedOptions && ( + + +

+ If enabled, end users on Windows hosts will be + required to set a BitLocker PIN. +

+
+

+ When the PIN is set, it’s required to unlock + Windows hosts during startup. +

+
+ } + > + Require BitLocker PIN + +
+ )} + + )} ( diff --git a/frontend/services/entities/disk_encryption.ts b/frontend/services/entities/disk_encryption.ts index 1d41103f3d..5d3cf8e71a 100644 --- a/frontend/services/entities/disk_encryption.ts +++ b/frontend/services/entities/disk_encryption.ts @@ -27,7 +27,11 @@ const diskEncryptionService = { } return sendRequest("GET", path); }, - updateDiskEncryption: (enableDiskEncryption: boolean, teamId?: number) => { + updateDiskEncryption: ( + enableDiskEncryption: boolean, + requireBitLockerPIN: boolean, + teamId?: number + ) => { // TODO - use same endpoint for both once issue with new endpoint for no team is resolved const { UPDATE_DISK_ENCRYPTION: teamsEndpoint, @@ -37,11 +41,13 @@ const diskEncryptionService = { return sendRequest("PATCH", noTeamsEndpoint, { mdm: { enable_disk_encryption: enableDiskEncryption, + windows_require_bitlocker_pin: requireBitLockerPIN, }, }); } return sendRequest("POST", teamsEndpoint, { enable_disk_encryption: enableDiskEncryption, + windows_require_bitlocker_pin: requireBitLockerPIN, // TODO - it would be good to be able to use an API_CONTEXT_NO_TEAM_ID here, but that is // currently set to 0, which should actually be undefined since the server expects teamId == // nil for no teams, not 0. diff --git a/webpack.config.js b/webpack.config.js index e99b2b67ca..0d4a250a9f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,6 +27,9 @@ let plugins = [ new webpack.DefinePlugin({ featureFlags: { // e.g.: allowGitOpsMode: JSON.stringify(process.env.ALLOW_GITOPS_MODE), + showBitLockerPINOption: JSON.stringify( + process.env.SHOW_BITLOCKER_PIN_OPTION + ), }, }), ];