diff --git a/frontend/__mocks__/policyMock.ts b/frontend/__mocks__/policyMock.ts index 09b5995d49..ad569e31d9 100644 --- a/frontend/__mocks__/policyMock.ts +++ b/frontend/__mocks__/policyMock.ts @@ -24,7 +24,6 @@ const DEFAULT_POLICY_MOCK: IPolicyStats = { next_update_ms: 3600000, calendar_events_enabled: true, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, install_software: { name: "testSw0", software_title_id: 1, @@ -62,7 +61,6 @@ export const createMockPoliciesResponse = ( next_update_ms: 3600000, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, { id: 29090, @@ -86,7 +84,6 @@ export const createMockPoliciesResponse = ( next_update_ms: 3600000, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, { id: 136, @@ -112,7 +109,6 @@ export const createMockPoliciesResponse = ( next_update_ms: 3600000, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, ], }; diff --git a/frontend/components/CriticalPolicyBadge/CriticalPolicyBadge.tsx b/frontend/components/CriticalPolicyBadge/CriticalPolicyBadge.tsx new file mode 100644 index 0000000000..6650957fff --- /dev/null +++ b/frontend/components/CriticalPolicyBadge/CriticalPolicyBadge.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +import Icon from "components/Icon"; +import TooltipWrapper from "components/TooltipWrapper"; + +const baseClass = "critical-badge"; + +const CriticalPolicyBadge = () => { + return ( +
+ + + +
+ ); +}; + +export default CriticalPolicyBadge; diff --git a/frontend/components/CriticalPolicyBadge/index.ts b/frontend/components/CriticalPolicyBadge/index.ts new file mode 100644 index 0000000000..f074959f48 --- /dev/null +++ b/frontend/components/CriticalPolicyBadge/index.ts @@ -0,0 +1 @@ +export { default } from "./CriticalPolicyBadge"; diff --git a/frontend/interfaces/policy.ts b/frontend/interfaces/policy.ts index 454b4c2681..fc657d5d5b 100644 --- a/frontend/interfaces/policy.ts +++ b/frontend/interfaces/policy.ts @@ -42,10 +42,10 @@ export interface IPolicy { team_id: number | null; created_at: string; updated_at: string; + // A critical policy cannot be "resolved later" if Okta conditional access is enabled for it critical: boolean; calendar_events_enabled: boolean; conditional_access_enabled: boolean; - conditional_access_bypass_enabled: boolean; install_software?: IPolicySoftwareToInstall; run_script?: Pick; labels_include_any?: ILabelPolicy[]; @@ -105,6 +105,7 @@ export interface ILoadTeamPolicyResponse { export interface IPolicyFormData { description?: string | number | boolean | undefined; resolution?: string | number | boolean | undefined; + // A critical policy cannot be "resolved later" if Okta conditional access is enabled for it critical?: boolean; platform?: CommaSeparatedPlatformString; name?: string | number | boolean | undefined; @@ -113,7 +114,6 @@ export interface IPolicyFormData { id?: number; calendar_events_enabled?: boolean; conditional_access_enabled?: boolean; - conditional_access_bypass_enabled?: boolean; software_title_id?: number | null; // null for PATCH to unset - note asymmetry with GET/LIST - see IPolicy.run_script script_id?: number | null; diff --git a/frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tsx b/frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tsx index 3afa98bfa2..6f8bafc8f7 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/ConditionalAccess/ConditionalAccess.tsx @@ -551,15 +551,15 @@ const ConditionalAccess = () => { - When enabled, disables the per-policy setting to allow - bypassing Okta conditional access.{" "} + Disables bypassing Okta conditional access for non-critical + policies.{" "} (Default: Off)

Bypassing is valid for a single login attempt and is tracked - in audit logs. + in audit logs. Critical policies can never be bypassed. } showArrow={false} diff --git a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx index 4c1f5b60be..dc6926fa53 100644 --- a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx +++ b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tests.tsx @@ -556,7 +556,6 @@ describe("Device User Page", () => { critical: false, calendar_events_enabled: false, conditional_access_enabled: true, - conditional_access_bypass_enabled: true, response: "fail", }); diff --git a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx index e496eb6601..31ae20fc62 100644 --- a/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx +++ b/frontend/pages/hosts/details/DeviceUserPage/DeviceUserPage.tsx @@ -673,11 +673,8 @@ const DeviceUserPage = ({ ); } - const hasAnyBypassDisabledFailingCAPolicy = host?.policies.some( - (p) => - p.response === "fail" && - p.conditional_access_enabled && - !p.conditional_access_bypass_enabled + const hasAnyCriticalFailingCAPolicy = host?.policies.some( + (p) => p.response === "fail" && p.conditional_access_enabled && p.critical ); return ( @@ -847,8 +844,7 @@ const DeviceUserPage = ({ onResolveLater={ globalConfig?.features?.enable_conditional_access && globalConfig.features?.enable_conditional_access_bypass && - !hasAnyBypassDisabledFailingCAPolicy && - selectedPolicy?.conditional_access_bypass_enabled + !hasAnyCriticalFailingCAPolicy ? () => { onCancelPolicyDetailsModal(); setShowBypassModal(true); diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx index 2dc553f227..e01f726aec 100644 --- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx @@ -789,8 +789,6 @@ const ManagePolicyPage = ({ const policyPromises = changedPolicies.map((changedPolicy) => { return teamPoliciesAPI.update(changedPolicy.id, { conditional_access_enabled: changedPolicy.conditional_access_enabled, - conditional_access_bypass_enabled: - changedPolicy.conditional_access_bypass_enabled, team_id: teamIdForApi, }); }); diff --git a/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx index 0d1cf38bc3..f73aa037b0 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/ConditionalAccessModal.tsx @@ -1,18 +1,13 @@ import React, { useContext, useRef, useState } from "react"; -import { - FLEET_WEBSITE_URL, - LEARN_MORE_ABOUT_BASE_LINK, -} from "utilities/constants"; - -import { isOktaConditionalAccessConfigured } from "interfaces/config"; +import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants"; +import TooltipTruncatedText from "components/TooltipTruncatedText"; +import CriticalPolicyBadge from "components/CriticalPolicyBadge"; import CustomLink from "components/CustomLink"; import Modal from "components/Modal"; import Button from "components/buttons/Button"; import Slider from "components/forms/fields/Slider"; -import Checkbox from "components/forms/fields/Checkbox"; -import TooltipWrapper from "components/TooltipWrapper"; import { AppContext } from "context/app"; import { IPaginatedListHandle } from "components/PaginatedList"; import PoliciesPaginatedList, { @@ -53,7 +48,7 @@ const ConditionalAccessModal = ({ }); const paginatedListRef = useRef>(null); - const { isGlobalAdmin, isTeamAdmin, config } = useContext(AppContext); + const { isGlobalAdmin, isTeamAdmin } = useContext(AppContext); const isAdmin = isGlobalAdmin || isTeamAdmin; const onChangeEnabled = () => { @@ -82,58 +77,12 @@ const ConditionalAccessModal = ({ /> ); - const renderItemRow = ( - item: IFormPolicy, - onChange: (item: IFormPolicy) => void - ) => { - const shouldShowCheckbox = - item.conditional_access_enabled && - // currently redundant as only darwin-targeting policies are enabled in this list - item.platform.includes("darwin") && - isOktaConditionalAccessConfigured(config) && - !config?.conditional_access?.bypass_disabled; - - if (!shouldShowCheckbox) { - return null; - } - - return ( - { - e.stopPropagation(); - }} - > - { - onChange({ - ...item, - conditional_access_bypass_enabled: !item.conditional_access_bypass_enabled, - }); - }} - > - - Allows end users to bypass conditional access for a single login - if they are unable to resolve the failing policy. -
-
- - This experimental setting will be removed in Fleet 4.83, and - only non-critical policies will allow bypass. For a seamless - upgrade, please avoid enabling bypass for policies marked - critical. - - - } - > - End users can bypass -
-
-
- ); - }; + const renderItemLabel = (policy: IFormPolicy) => ( + <> + + {policy.critical && } + + ); const renderConfigured = () => { return ( @@ -147,19 +96,13 @@ const ConditionalAccessModal = ({ activeText="Enabled" disabled={gitOpsModeEnabled || !isAdmin} /> - { item.conditional_access_enabled = !item.conditional_access_enabled; return item; diff --git a/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss index e2b7fedaad..37d9700cc4 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss +++ b/frontend/pages/policies/ManagePoliciesPage/components/ConditionalAccessModal/_styles.scss @@ -6,4 +6,7 @@ white-space: nowrap; } } + .paginated-list .fleet-checkbox__label { + gap: 0.5rem; + } } diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesPaginatedList/PoliciesPaginatedList.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesPaginatedList/PoliciesPaginatedList.tsx index 6c14451b08..c4847b926b 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesPaginatedList/PoliciesPaginatedList.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesPaginatedList/PoliciesPaginatedList.tsx @@ -45,6 +45,7 @@ interface IPoliciesPaginatedListProps { item: IFormPolicy, onChange: (item: IFormPolicy) => void ) => ReactElement | false | null | undefined; + renderItemLabel?: (item: IFormPolicy) => ReactElement | null; onToggleItem: (item: IFormPolicy) => IFormPolicy; /** A function defining the conditions under which to disable a policy. */ getPolicyDisabled?: (policy: IFormPolicy) => boolean; @@ -64,6 +65,7 @@ function PoliciesPaginatedList( { isSelected, renderItemRow, + renderItemLabel, onToggleItem, getPolicyDisabled, getPolicyTooltipContent, @@ -238,6 +240,7 @@ function PoliciesPaginatedList( isItemDisabled={getPolicyDisabled} getItemTooltipContent={getPolicyTooltipContent} onClickRow={onToggleItem} + renderItemLabel={renderItemLabel} renderItemRow={renderItemRow} pageSize={DEFAULT_PAGE_SIZE} onUpdate={onUpdate} diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx index e143983571..f478d91d0e 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx @@ -8,7 +8,6 @@ import { Tooltip as ReactTooltip5 } from "react-tooltip-5"; import Checkbox from "components/forms/fields/Checkbox"; import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; import LinkCell from "components/TableContainer/DataTable/LinkCell/LinkCell"; -import Icon from "components/Icon"; import { IPolicyStats } from "interfaces/policy"; import PATHS from "router/paths"; @@ -16,6 +15,7 @@ import { getPathWithQueryParams } from "utilities/url"; import sortUtils from "utilities/sort"; import { PolicyResponse } from "utilities/constants"; +import CriticalPolicyBadge from "components/CriticalPolicyBadge"; import InheritedBadge from "components/InheritedBadge"; import { getConditionalSelectHeaderCheckboxProps } from "components/TableContainer/utilities/config_utils"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; @@ -116,32 +116,7 @@ const generateTableHeaders = ( value={cellProps.cell.value} suffix={ <> - {isPremiumTier && critical && ( -
- - - - - This policy has been marked as critical. - -
- )} + {isPremiumTier && critical && } {viewingTeamPolicies && team_id === null && ( )} diff --git a/frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx b/frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx index 485e9a2369..949311c966 100644 --- a/frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/helpers.tests.tsx @@ -28,7 +28,6 @@ describe("getInstallSoftwareErrorMessage", () => { critical: false, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, { swIdToInstall: 456, @@ -50,7 +49,6 @@ describe("getInstallSoftwareErrorMessage", () => { critical: false, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, ]; @@ -162,7 +160,6 @@ describe("getRunScriptErrorMessage", () => { critical: false, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, { scriptIdToRun: 456, @@ -184,7 +181,6 @@ describe("getRunScriptErrorMessage", () => { critical: false, calendar_events_enabled: false, conditional_access_enabled: false, - conditional_access_bypass_enabled: true, }, ]; diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx index 3577aaa289..a65324a8f7 100644 --- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx +++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx @@ -641,8 +641,9 @@ const PolicyForm = ({ - If automations are turned on, this -
information is included. + If automations are turned on, this information is included. If + Okta conditional access is configured, end users can never + bypass critical policies.

} > diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx b/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx index 1c515be7d3..97fd55f9c2 100644 --- a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx +++ b/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx @@ -313,8 +313,9 @@ const SaveNewPolicyModal = ({ - If automations are turned on, this -
information is included. + If automations are turned on, this information is + included. If Okta conditional access is configured, end + users can never bypass critical policies.

} > diff --git a/frontend/services/entities/team_policies.ts b/frontend/services/entities/team_policies.ts index 691e82071d..a528183282 100644 --- a/frontend/services/entities/team_policies.ts +++ b/frontend/services/entities/team_policies.ts @@ -97,7 +97,6 @@ export default { // automations-related fields calendar_events_enabled, conditional_access_enabled, - conditional_access_bypass_enabled, software_title_id, script_id, labels_include_any, @@ -115,7 +114,6 @@ export default { critical, calendar_events_enabled, conditional_access_enabled, - conditional_access_bypass_enabled, software_title_id, script_id, labels_include_any,