Conditional access dropdown and copy (#37338)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #37305 

Resolves issue that conditional access modal was not visible for
self-hosted and contained wrong copy.

## Testing

- [x] QA'd all new/changed functionality manually

For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Improvements**
* "Conditional access" automation option is now available to all users
without license restrictions.
* Enhanced conditional access detection for both Okta and Microsoft
Entra identity providers.
* Improved conditional access setup process with clearer provider
information display based on your configuration.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Victor Lyuboslavsky
2025-12-18 10:37:47 -06:00
committed by GitHub
parent d74706efaf
commit c78c63be70
5 changed files with 56 additions and 29 deletions
+32
View File
@@ -272,3 +272,35 @@ export interface IGitOpsModeConfig {
gitops_mode_enabled: boolean;
repository_url: string;
}
/** Check if Okta conditional access is configured (all 4 fields must be present) */
export const isOktaConditionalAccessConfigured = (
config: IConfig | null | undefined
): boolean => {
const ca = config?.conditional_access;
return !!(
ca?.okta_idp_id &&
ca?.okta_assertion_consumer_service_url &&
ca?.okta_audience_uri &&
ca?.okta_certificate
);
};
/** Check if Microsoft Entra conditional access is configured */
export const isEntraConditionalAccessConfigured = (
config: IConfig | null | undefined
): boolean => {
return (
config?.conditional_access?.microsoft_entra_connection_configured ?? false
);
};
/** Check if any conditional access provider is configured (Okta or Entra) */
export const isConditionalAccessConfigured = (
config: IConfig | null | undefined
): boolean => {
return (
isOktaConditionalAccessConfigured(config) ||
isEntraConditionalAccessConfigured(config)
);
};
@@ -25,7 +25,7 @@ import PremiumFeatureMessage from "components/PremiumFeatureMessage";
import { useQuery } from "react-query";
import DataError from "components/DataError";
import Modal from "components/Modal";
import { IConfig } from "interfaces/config";
import { IConfig, isOktaConditionalAccessConfigured } from "interfaces/config";
import SectionCard from "../MdmSettings/components/SectionCard";
import EntraConditionalAccessModal from "./components/EntraConditionalAccessModal";
@@ -245,19 +245,9 @@ const ConditionalAccess = () => {
const {
microsoft_entra_tenant_id: entraTenantId,
microsoft_entra_connection_configured: entraConfigured,
okta_idp_id: oktaIdpId,
okta_assertion_consumer_service_url: oktaAcsUrl,
okta_audience_uri: oktaAudienceUri,
okta_certificate: oktaCertificate,
} = config?.conditional_access || {};
// Determine if Okta is configured (all 4 fields must be present)
const oktaConfigured = !!(
oktaIdpId &&
oktaAcsUrl &&
oktaAudienceUri &&
oktaCertificate
);
const oktaConfigured = isOktaConditionalAccessConfigured(config);
// Check if this is a managed cloud deployment (Microsoft Entra requires proxy infrastructure)
const isManagedCloud = config?.license?.managed_cloud || false;
@@ -59,18 +59,18 @@ const validate = (formData: IFormData) => {
errs[OKTA_IDP_ID] = `IdP ID must be ${maxURLLength} characters or less`;
}
// Assertion Consumer Service URL validation
// Assertion consumer service URL validation
if (!formData[OKTA_ACS_URL] || !formData[OKTA_ACS_URL].trim()) {
errs[OKTA_ACS_URL] = "Assertion Consumer Service URL must be present";
errs[OKTA_ACS_URL] = "Assertion consumer service URL must be present";
} else if (formData[OKTA_ACS_URL].length > maxURLLength) {
errs[
OKTA_ACS_URL
] = `Assertion Consumer Service URL must be ${maxURLLength} characters or less`;
] = `Assertion consumer service URL must be ${maxURLLength} characters or less`;
} else if (
!valid_url({ url: formData[OKTA_ACS_URL], protocols: ["http", "https"] })
) {
errs[OKTA_ACS_URL] =
"Assertion Consumer Service URL must be a valid URL with http or https scheme and a host";
"Assertion consumer service URL must be a valid URL with http or https scheme and a host";
}
// Audience URI validation
@@ -12,7 +12,11 @@ import { PolicyContext } from "context/policy";
import { TableContext } from "context/table";
import { NotificationContext } from "context/notification";
import useTeamIdParam from "hooks/useTeamIdParam";
import { IConfig, IWebhookSettings } from "interfaces/config";
import {
IConfig,
IWebhookSettings,
isConditionalAccessConfigured,
} from "interfaces/config";
import { IZendeskJiraIntegrations } from "interfaces/integration";
import { INotification } from "interfaces/notification";
import {
@@ -121,6 +125,11 @@ const ManagePolicyPage = ({
} = useContext(AppContext);
const isPrimoMode =
globalConfigFromContext?.partnerships?.enable_primo || false;
const isManagedCloud =
globalConfigFromContext?.license?.managed_cloud || false;
const conditionalAccessProviderText = isManagedCloud
? "Okta or Microsoft Entra"
: "Okta";
const { renderFlash, renderMultiFlash } = useContext(NotificationContext);
const { setResetSelectedRows } = useContext(TableContext);
@@ -1115,10 +1124,6 @@ const ManagePolicyPage = ({
const isCalEventsEnabled =
teamConfig?.integrations.google_calendar?.enable_calendar_events ?? false;
const isConditionalAccessConfigured =
globalConfig?.conditional_access?.microsoft_entra_connection_configured ??
false;
const isConditionalAccessEnabled =
(teamIdForApi === API_NO_TEAM_ID
? globalConfig?.integrations.conditional_access_enabled
@@ -1198,17 +1203,14 @@ const ManagePolicyPage = ({
helpText: "Run script to resolve failing policies.",
tooltipContent: disabledRunScriptTooltipContent,
},
];
if (globalConfigFromContext?.license.managed_cloud) {
options.push({
{
label: "Conditional access",
value: "conditional_access",
isDisabled: !!disabledConditionalAccessTooltipContent,
helpText: "Block single sign-on for hosts failing policies.",
tooltipContent: disabledConditionalAccessTooltipContent,
});
}
},
];
// Maintainers do not have access to other workflows
if (!isGlobalMaintainer && !isTeamMaintainer) {
@@ -1391,11 +1393,12 @@ const ManagePolicyPage = ({
<ConditionalAccessModal
onExit={toggleConditionalAccessModal}
onSubmit={onUpdateConditionalAccess}
configured={isConditionalAccessConfigured}
configured={isConditionalAccessConfigured(globalConfig)}
enabled={isConditionalAccessEnabled}
isUpdating={isUpdatingPolicies}
gitOpsModeEnabled={gitOpsModeEnabled}
teamId={currentTeamId ?? 0}
providerText={conditionalAccessProviderText}
/>
)}
</>
@@ -30,6 +30,7 @@ interface IConditionalAccessModal {
isUpdating: boolean;
gitOpsModeEnabled?: boolean;
teamId: number;
providerText: string;
}
const ConditionalAccessModal = ({
@@ -40,6 +41,7 @@ const ConditionalAccessModal = ({
isUpdating,
gitOpsModeEnabled = false,
teamId,
providerText,
}: IConditionalAccessModal) => {
const [formData, setFormData] = useState<IConditionalAccessFormData>({
enabled,
@@ -130,7 +132,7 @@ const ConditionalAccessModal = ({
const renderNotConfigured = () => (
<>
To block single sign-on from hosts failing policies, you must first
connect Fleet to Microsoft Entra.
connect Fleet to {providerText}.
<br />
<br />
This can be configured in <b>Settings</b> &gt; <b>Integrations</b> &gt;{" "}