mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Deleting Okta from Fleet won't unblock users. New website redirect is in a separate PR: https://github.com/fleetdm/fleet/pull/36015/files#diff-0c6120927d3e65309562b5b15b261d3298d05fcb0ff18e5b6512ee683b7ad6b3 Context: https://github.com/fleetdm/fleet/pull/35204#discussion_r2550609592
431 lines
13 KiB
TypeScript
431 lines
13 KiB
TypeScript
import React from "react";
|
|
|
|
import { screen, waitFor } from "@testing-library/react";
|
|
import { http, HttpResponse } from "msw";
|
|
|
|
import createMockConfig from "__mocks__/configMock";
|
|
import mockServer from "test/mock-server";
|
|
import { baseUrl, createCustomRenderer } from "test/test-utils";
|
|
|
|
import ConditionalAccess from "./ConditionalAccess";
|
|
|
|
const triggerConditionalAccessHandler = http.post(
|
|
baseUrl("/conditional-access/microsoft"),
|
|
() => {
|
|
return HttpResponse.json({
|
|
microsoft_authentication_url: "https://example.com",
|
|
});
|
|
}
|
|
);
|
|
|
|
const updateConfigHandler = http.patch(baseUrl("/config"), () => {
|
|
return HttpResponse.json(
|
|
createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "",
|
|
microsoft_entra_connection_configured: false,
|
|
okta_idp_id: "okta-idp-123",
|
|
okta_assertion_consumer_service_url: "https://example.com/acs",
|
|
okta_audience_uri: "https://example.com",
|
|
okta_certificate: "cert-data",
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
// Helper to create a config with empty conditional access settings
|
|
const createEmptyConditionalAccessConfig = () =>
|
|
createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "",
|
|
microsoft_entra_connection_configured: false,
|
|
okta_idp_id: "",
|
|
okta_assertion_consumer_service_url: "",
|
|
okta_audience_uri: "",
|
|
okta_certificate: "",
|
|
},
|
|
});
|
|
|
|
describe("Conditional access", () => {
|
|
describe("Not configured", () => {
|
|
it("Renders both integration cards when nothing is configured", () => {
|
|
const mockConfig = createEmptyConditionalAccessConfig();
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(screen.getByText("Okta")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Connect Okta to enable conditional access.")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Microsoft Entra")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Connect Entra to enable conditional access.")
|
|
).toBeInTheDocument();
|
|
// Should have two Connect buttons
|
|
expect(screen.getAllByText("Connect")).toHaveLength(2);
|
|
});
|
|
|
|
it("Opens the Entra modal when clicking Connect on Entra card", async () => {
|
|
const mockConfig = createEmptyConditionalAccessConfig();
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { user } = render(<ConditionalAccess />);
|
|
|
|
// Click the second Connect button (Microsoft Entra)
|
|
const connectButtons = screen.getAllByText("Connect");
|
|
await user.click(connectButtons[1]);
|
|
|
|
// Modal should open
|
|
expect(
|
|
screen.getByText("Microsoft Entra conditional access")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Microsoft Entra tenant ID")).toBeInTheDocument();
|
|
});
|
|
|
|
it("Triggers Microsoft auth flow when submitting Entra modal", async () => {
|
|
const mockConfig = createEmptyConditionalAccessConfig();
|
|
|
|
mockServer.use(triggerConditionalAccessHandler);
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { user } = render(<ConditionalAccess />);
|
|
|
|
// Open modal
|
|
const connectButtons = screen.getAllByText("Connect");
|
|
await user.click(connectButtons[1]);
|
|
|
|
// Fill in tenant ID
|
|
const input = screen.getByRole("textbox");
|
|
await user.type(input, "abcdefg");
|
|
|
|
// Submit form
|
|
const saveButton = screen.getByRole("button", { name: "Save" });
|
|
await user.click(saveButton);
|
|
|
|
// Should show the "continue in new tab" message
|
|
await waitFor(() => {
|
|
expect(
|
|
screen.getByText(
|
|
/To complete your integration, follow the instructions in the other tab/
|
|
)
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("Opens the Okta modal when clicking Connect on Okta card", async () => {
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { user } = render(<ConditionalAccess />);
|
|
|
|
// Click the first Connect button (Okta)
|
|
const connectButtons = screen.getAllByText("Connect");
|
|
await user.click(connectButtons[0]);
|
|
|
|
// Modal should open with new Figma structure
|
|
expect(screen.getByText("Okta conditional access")).toBeInTheDocument();
|
|
// Check for new sections
|
|
expect(
|
|
screen.getByText("Identity provider (IdP) signature certificate")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("User scope profile")).toBeInTheDocument();
|
|
// Check for input fields
|
|
expect(screen.getByText("IdP ID")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Assertion consumer service URL")
|
|
).toBeInTheDocument();
|
|
expect(screen.getByText("Audience URI")).toBeInTheDocument();
|
|
// Check for certificate upload section
|
|
expect(screen.getByText("Okta certificate")).toBeInTheDocument();
|
|
});
|
|
|
|
it("Saves Okta configuration when submitting form", async () => {
|
|
const mockConfig = createEmptyConditionalAccessConfig();
|
|
|
|
mockServer.use(updateConfigHandler);
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { user } = render(<ConditionalAccess />);
|
|
|
|
// Open modal
|
|
const connectButtons = screen.getAllByText("Connect");
|
|
await user.click(connectButtons[0]);
|
|
|
|
// Wait for modal to open
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Okta conditional access")).toBeInTheDocument();
|
|
});
|
|
|
|
// Fill in text fields
|
|
// Note: First textarea is the read-only User scope profile
|
|
const textboxes = screen.getAllByRole("textbox");
|
|
await user.type(textboxes[1], "okta-idp-123"); // IdP ID
|
|
await user.type(textboxes[2], "https://example.com/acs"); // ACS URL
|
|
await user.type(textboxes[3], "https://example.com"); // Audience URI
|
|
|
|
// Upload certificate file
|
|
const certificateContent = `-----BEGIN CERTIFICATE-----
|
|
MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKm7MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
|
-----END CERTIFICATE-----`;
|
|
|
|
const file = new File([certificateContent], "certificate.pem", {
|
|
type: "application/x-pem-file",
|
|
});
|
|
|
|
const fileInput = document.querySelector(
|
|
'input[type="file"]'
|
|
) as HTMLInputElement;
|
|
await user.upload(fileInput, file);
|
|
|
|
// Wait for file to be processed
|
|
await waitFor(() => {
|
|
expect(screen.getByText("certificate.pem")).toBeInTheDocument();
|
|
});
|
|
|
|
// Submit form
|
|
const saveButton = screen.getByRole("button", { name: "Save" });
|
|
await user.click(saveButton);
|
|
|
|
// Should show success message and close modal
|
|
await waitFor(() => {
|
|
expect(
|
|
screen.queryByText("Okta conditional access")
|
|
).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Confirming configured", () => {
|
|
it("Renders a spinner when Entra tenant id is present but configuration not yet confirmed", () => {
|
|
const mockConfig = createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "abcdefg",
|
|
microsoft_entra_connection_configured: false,
|
|
okta_idp_id: "",
|
|
okta_assertion_consumer_service_url: "",
|
|
okta_audience_uri: "",
|
|
okta_certificate: "",
|
|
},
|
|
});
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(screen.getByTestId("spinner")).toBeVisible();
|
|
});
|
|
});
|
|
|
|
describe("Configured", () => {
|
|
it("Shows Entra as configured when connection is confirmed", async () => {
|
|
const mockConfig = createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "abcdefg",
|
|
microsoft_entra_connection_configured: true,
|
|
okta_idp_id: "",
|
|
okta_assertion_consumer_service_url: "",
|
|
okta_audience_uri: "",
|
|
okta_certificate: "",
|
|
},
|
|
});
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(
|
|
screen.getByText("Microsoft Entra conditional access configured")
|
|
).toBeInTheDocument();
|
|
// Should only have Delete button for Entra (no Edit button per Figma design)
|
|
expect(screen.getByText("Delete")).toBeInTheDocument();
|
|
expect(screen.queryByText("Edit")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("Shows Okta as configured when all Okta fields are present", async () => {
|
|
const mockConfig = createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "",
|
|
microsoft_entra_connection_configured: false,
|
|
okta_idp_id: "okta-idp-123",
|
|
okta_assertion_consumer_service_url: "https://example.com/acs",
|
|
okta_audience_uri: "https://example.com",
|
|
okta_certificate: "cert-data",
|
|
},
|
|
});
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(
|
|
screen.getByText("Okta conditional access configured")
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("Shows both providers as configured when both are set up", async () => {
|
|
const mockConfig = createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "abcdefg",
|
|
microsoft_entra_connection_configured: true,
|
|
okta_idp_id: "okta-idp-123",
|
|
okta_assertion_consumer_service_url: "https://example.com/acs",
|
|
okta_audience_uri: "https://example.com",
|
|
okta_certificate: "cert-data",
|
|
},
|
|
});
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(
|
|
screen.getByText("Okta conditional access configured")
|
|
).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Microsoft Entra conditional access configured")
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("Shows delete confirmation modal when clicking Delete on Okta", async () => {
|
|
const mockConfig = createMockConfig({
|
|
conditional_access: {
|
|
microsoft_entra_tenant_id: "",
|
|
microsoft_entra_connection_configured: false,
|
|
okta_idp_id: "okta-idp-123",
|
|
okta_assertion_consumer_service_url: "https://example.com/acs",
|
|
okta_audience_uri: "https://example.com",
|
|
okta_certificate: "cert-data",
|
|
},
|
|
});
|
|
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: true,
|
|
config: mockConfig,
|
|
},
|
|
},
|
|
});
|
|
|
|
const { user } = render(<ConditionalAccess />);
|
|
|
|
// Should show configured state
|
|
expect(
|
|
screen.getByText("Okta conditional access configured")
|
|
).toBeInTheDocument();
|
|
|
|
// Click Delete button (first one is for Okta)
|
|
const deleteButton = screen.getAllByText("Delete")[0];
|
|
await user.click(deleteButton);
|
|
|
|
// Should show delete confirmation modal
|
|
await waitFor(() => {
|
|
expect(
|
|
screen.getByText(/Before you delete, first unblock all end users/)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
// Modal should have Delete and Cancel buttons
|
|
expect(
|
|
screen.getAllByRole("button", { name: "Delete" }).length
|
|
).toBeGreaterThan(0);
|
|
expect(
|
|
screen.getByRole("button", { name: "Cancel" })
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("Premium tier", () => {
|
|
it("Shows premium feature message when not premium tier", () => {
|
|
const render = createCustomRenderer({
|
|
withBackendMock: true,
|
|
context: {
|
|
app: {
|
|
isPremiumTier: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
render(<ConditionalAccess />);
|
|
|
|
expect(
|
|
screen.getByText(/This feature is included in Fleet Premium/i)
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|