UI: Remove experimental conditional_access_bypass_enabled policy setting in favor of critical (#40924)
**Related issue:** Resolves #40417 <img width="1624" height="1061" alt="Screenshot 2026-03-03 at 4 17 19 PM" src="https://github.com/user-attachments/assets/feb3f4cd-af20-42be-887a-73ccef4f2b23" /> <img width="1624" height="1061" alt="Screenshot 2026-03-03 at 4 19 17 PM" src="https://github.com/user-attachments/assets/0b67e5df-525f-444d-8521-f1de1527cfaa" /> <img width="1624" height="1061" alt="Screenshot 2026-03-03 at 4 19 26 PM" src="https://github.com/user-attachments/assets/922f6f05-4889-430f-9da9-3f8f0ac5a11c" /> <img width="1624" height="1061" alt="Screenshot 2026-03-03 at 4 19 45 PM" src="https://github.com/user-attachments/assets/65445602-8a83-429f-aaaf-729f127c4c4b" /> - [x] Changes file added for user-visible changes in `changes/` - [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 ## Release Notes * **New Features** * Added visual badge to identify critical policies. * **Improvements** * Critical policies can no longer be bypassed when Okta conditional access is configured. * Simplified conditional access policy management UI by removing per-policy bypass toggles. * Updated tooltips to clarify bypass restrictions for critical policies. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<div className={baseClass}>
|
||||
<TooltipWrapper
|
||||
tipContent="This policy has been marked as critical."
|
||||
showArrow
|
||||
position="top"
|
||||
tipOffset={8}
|
||||
underline={false}
|
||||
fixedPositionStrategy
|
||||
>
|
||||
<Icon
|
||||
className="critical-policy-icon"
|
||||
name="policy"
|
||||
size="small"
|
||||
color="ui-fleet-black-75"
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CriticalPolicyBadge;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./CriticalPolicyBadge";
|
||||
@@ -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<IScript, "id" | "name">;
|
||||
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;
|
||||
|
||||
@@ -551,15 +551,15 @@ const ConditionalAccess = () => {
|
||||
<TooltipWrapper
|
||||
tipContent={
|
||||
<>
|
||||
When enabled, disables the per-policy setting to allow
|
||||
bypassing Okta conditional access.{" "}
|
||||
Disables bypassing Okta conditional access for non-critical
|
||||
policies.{" "}
|
||||
<em>
|
||||
(Default: <strong>Off</strong>)
|
||||
</em>
|
||||
<br />
|
||||
<br />
|
||||
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}
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
+11
-68
@@ -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<IPaginatedListHandle<IFormPolicy>>(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 (
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
value={item.conditional_access_bypass_enabled}
|
||||
onChange={() => {
|
||||
onChange({
|
||||
...item,
|
||||
conditional_access_bypass_enabled: !item.conditional_access_bypass_enabled,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<TooltipWrapper
|
||||
tipContent={
|
||||
<>
|
||||
Allows end users to bypass conditional access for a single login
|
||||
if they are unable to resolve the failing policy.
|
||||
<br />
|
||||
<br />
|
||||
<em>
|
||||
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.
|
||||
</em>
|
||||
</>
|
||||
}
|
||||
>
|
||||
End users can bypass
|
||||
</TooltipWrapper>
|
||||
</Checkbox>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const renderItemLabel = (policy: IFormPolicy) => (
|
||||
<>
|
||||
<TooltipTruncatedText value={policy.name} />
|
||||
{policy.critical && <CriticalPolicyBadge />}
|
||||
</>
|
||||
);
|
||||
|
||||
const renderConfigured = () => {
|
||||
return (
|
||||
@@ -147,19 +96,13 @@ const ConditionalAccessModal = ({
|
||||
activeText="Enabled"
|
||||
disabled={gitOpsModeEnabled || !isAdmin}
|
||||
/>
|
||||
<CustomLink
|
||||
text="Preview end user experience"
|
||||
newTab
|
||||
multiline={false}
|
||||
url={`${FLEET_WEBSITE_URL}/microsoft-compliance-partner/remediate`}
|
||||
/>
|
||||
</span>
|
||||
<PoliciesPaginatedList
|
||||
ref={paginatedListRef}
|
||||
isSelected="conditional_access_enabled"
|
||||
getPolicyDisabled={getPolicyDisabled}
|
||||
getPolicyTooltipContent={getPolicyTooltipContent}
|
||||
renderItemRow={renderItemRow}
|
||||
renderItemLabel={renderItemLabel}
|
||||
onToggleItem={(item: IFormPolicy) => {
|
||||
item.conditional_access_enabled = !item.conditional_access_enabled;
|
||||
return item;
|
||||
|
||||
+3
@@ -6,4 +6,7 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.paginated-list .fleet-checkbox__label {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -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}
|
||||
|
||||
+2
-27
@@ -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 && (
|
||||
<div className="critical-badge">
|
||||
<span
|
||||
className="critical-badge-icon"
|
||||
data-tooltip-id={`critical-tooltip-${id}`}
|
||||
>
|
||||
<Icon
|
||||
className="critical-policy-icon"
|
||||
name="policy"
|
||||
size="small"
|
||||
color="ui-fleet-black-75"
|
||||
/>
|
||||
</span>
|
||||
<ReactTooltip5
|
||||
className="critical-tooltip"
|
||||
disableStyleInjection
|
||||
place="top"
|
||||
opacity={1}
|
||||
id={`critical-tooltip-${id}`}
|
||||
offset={8}
|
||||
positionStrategy="fixed"
|
||||
>
|
||||
This policy has been marked as critical.
|
||||
</ReactTooltip5>
|
||||
</div>
|
||||
)}
|
||||
{isPremiumTier && critical && <CriticalPolicyBadge />}
|
||||
{viewingTeamPolicies && team_id === null && (
|
||||
<InheritedBadge tooltipContent="This policy runs on all hosts." />
|
||||
)}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -641,8 +641,9 @@ const PolicyForm = ({
|
||||
<TooltipWrapper
|
||||
tipContent={
|
||||
<p>
|
||||
If automations are turned on, this
|
||||
<br /> 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.
|
||||
</p>
|
||||
}
|
||||
>
|
||||
|
||||
+3
-2
@@ -313,8 +313,9 @@ const SaveNewPolicyModal = ({
|
||||
<TooltipWrapper
|
||||
tipContent={
|
||||
<p>
|
||||
If automations are turned on, this
|
||||
<br /> 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.
|
||||
</p>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user