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:
@@ -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)
|
||||
);
|
||||
};
|
||||
|
||||
+2
-12
@@ -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;
|
||||
|
||||
+4
-4
@@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
+3
-1
@@ -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> > <b>Integrations</b> >{" "}
|
||||
|
||||
Reference in New Issue
Block a user