fleet/frontend/pages/admin/OrgSettingsPage/OrgSettingsPage.tsx
Ian Littman 724860bd6e
Move end user authentication to a tab under Integrations > SSO (#35084)
Resolves #34525.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] QA'd all new/changed functionality manually
2025-11-05 14:34:59 -06:00

152 lines
4.6 KiB
TypeScript

import React, { useCallback, useContext, useState } from "react";
import { InjectedRouter, Params } from "react-router/lib/Router";
import { useQuery } from "react-query";
import { useErrorHandler } from "react-error-boundary";
import { IConfig } from "interfaces/config";
import { IApiError } from "interfaces/errors";
import configAPI from "services/entities/config";
import { AppContext } from "context/app";
import { NotificationContext } from "context/notification";
import deepDifference from "utilities/deep_difference";
import Spinner from "components/Spinner";
import paths from "router/paths";
import SideNav from "../components/SideNav";
import ORG_SETTINGS_NAV_ITEMS from "./OrgSettingsNavItems";
import { DeepPartial } from "./cards/constants";
interface IOrgSettingsPageProps {
params: Params;
router: InjectedRouter; // v3
}
export const baseClass = "org-settings";
const OrgSettingsPage = ({ params, router }: IOrgSettingsPageProps) => {
const { section } = params;
const DEFAULT_SETTINGS_SECTION = ORG_SETTINGS_NAV_ITEMS[0];
const [isUpdatingSettings, setIsUpdatingSettings] = useState(false);
const { isFreeTier, isPremiumTier, setConfig, isSandboxMode } = useContext(
AppContext
);
if (isSandboxMode) {
// redirect to Integrations page in sandbox mode
router.push(paths.ADMIN_INTEGRATIONS);
}
const { renderFlash } = useContext(NotificationContext);
const handlePageError = useErrorHandler();
const {
data: appConfig,
isLoading: isLoadingAppConfig,
refetch: refetchConfig,
} = useQuery<IConfig, Error, IConfig>(["config"], () => configAPI.loadAll(), {
select: (data: IConfig) => data,
onSuccess: (data) => {
setConfig(data);
},
});
const onFormSubmit = useCallback(
async (formUpdates: DeepPartial<IConfig>) => {
if (!appConfig) {
return false;
}
setIsUpdatingSettings(true);
const diff = deepDifference(formUpdates, appConfig);
// send all formUpdates.agent_options because diff overrides all agent options
diff.agent_options = formUpdates.agent_options;
try {
await configAPI.update(diff);
renderFlash("success", "Successfully updated settings.");
refetchConfig();
return true;
} catch (response) {
const resp = response as undefined | { data: IApiError };
if (resp?.data.errors[0].reason.includes("could not dial smtp host")) {
renderFlash(
"error",
"Could not connect to SMTP server. Please try again."
);
} else if (resp?.data.errors) {
const reason = resp?.data.errors[0].reason;
const agentOptionsInvalid =
reason.includes("unsupported key provided") ||
reason.includes("invalid value type");
const isAgentOptionsError =
agentOptionsInvalid ||
reason.includes("script_execution_timeout' value exceeds limit.");
renderFlash(
"error",
<>
Couldn&apos;t update{" "}
{isAgentOptionsError ? "agent options" : "settings"}: {reason}
{agentOptionsInvalid && (
<>
<br />
If you&apos;re not using the latest osquery, use the fleetctl
apply --force command to override validation.
</>
)}
</>
);
}
return false;
} finally {
setIsUpdatingSettings(false);
}
},
[appConfig, refetchConfig, renderFlash]
);
// filter out non-premium options
let navItems = ORG_SETTINGS_NAV_ITEMS;
if (!isPremiumTier) {
navItems = ORG_SETTINGS_NAV_ITEMS.filter(
(item) => item.urlSection !== "fleet-desktop"
);
}
const currentFormSection =
navItems.find((item) => item.urlSection === section) ??
DEFAULT_SETTINGS_SECTION;
const CurrentCard = currentFormSection.Card;
if (isFreeTier && section === "fleet-desktop") {
handlePageError({ status: 403 });
return null;
}
return (
<div className={`${baseClass}`}>
<SideNav
className={`${baseClass}__side-nav`}
navItems={navItems}
activeItem={currentFormSection.urlSection}
CurrentCard={
!isLoadingAppConfig && appConfig ? (
<CurrentCard
appConfig={appConfig}
handleSubmit={onFormSubmit}
isUpdatingSettings={isUpdatingSettings}
isPremiumTier={isPremiumTier}
router={router}
/>
) : (
<Spinner />
)
}
/>
</div>
);
};
export default OrgSettingsPage;