GitOps mode updates (#26733)

For #26719 

Set of updates based on feedback on the GitOps mode. 

- [X] On settings/teams/users, keep "Add user" button and "Actions"
dropdown enabled
- [X] Enable buttons on /settings/integrations/mdm/apple
- [x] Disable form fields (no tooltip) + save button (w/ tooltip) on
/controls/setup-experience/end-user-auth
- [x] Disable "Edit" and "Delete" actions w/ tooltip on software detail
page
- [x] Update Org Settings -> Advanced options to only disable items
available in gitops
  - Domain
  - Verify SSL certs
  - Enable STARTTLS
- [x] Disable adding fleet maintained apps
This commit is contained in:
Scott Gress
2025-03-12 11:03:12 -07:00
committed by GitHub
parent 20109fa7f8
commit 822fe3dd18
8 changed files with 337 additions and 218 deletions
@@ -3,10 +3,13 @@ import { Link } from "react-router";
import PATHS from "router/paths";
import mdmAPI from "services/entities/mdm";
import classnames from "classnames";
import Button from "components/buttons/Button";
import Checkbox from "components/forms/fields/Checkbox";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { NotificationContext } from "context/notification";
import { AppContext } from "context/app";
const baseClass = "end-user-auth-form";
@@ -20,6 +23,8 @@ const EndUserAuthForm = ({
defaultIsEndUserAuthEnabled,
}: IEndUserAuthFormProps) => {
const { renderFlash } = useContext(NotificationContext);
const gitOpsModeEnabled = useContext(AppContext).config?.gitops
.gitops_mode_enabled;
const [isEndUserAuthEnabled, setEndUserAuthEnabled] = useState(
defaultIsEndUserAuthEnabled
@@ -45,21 +50,34 @@ const EndUserAuthForm = ({
}
};
const classes = classnames({ [`${baseClass}--disabled`]: gitOpsModeEnabled });
return (
<div className={baseClass}>
<form>
<Checkbox value={isEndUserAuthEnabled} onChange={onToggleEndUserAuth}>
<Checkbox
disabled={gitOpsModeEnabled}
value={isEndUserAuthEnabled}
onChange={onToggleEndUserAuth}
>
Turn on
</Checkbox>
<p>
<p className={classes}>
Require end users to authenticate with your identity provider (IdP)
and agree to an end user license agreement (EULA) when they setup
their new macOS hosts.{" "}
<Link to={PATHS.ADMIN_INTEGRATIONS_MDM}>View IdP and EULA</Link>
</p>
<Button isLoading={isUpdating} onClick={onClickSave}>
Save
</Button>
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
disabled={disableChildren}
isLoading={isUpdating}
onClick={onClickSave}
>
Save
</Button>
)}
/>
</form>
</div>
);
@@ -1,4 +1,6 @@
import React, { useState } from "react";
import React, { useContext, useState } from "react";
import { AppContext } from "context/app";
import { ILabelSummary } from "interfaces/label";
@@ -13,6 +15,7 @@ import {
} from "pages/SoftwarePage/helpers";
import AdvancedOptionsFields from "pages/SoftwarePage/components/AdvancedOptionsFields";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { generateFormValidation } from "./helpers";
@@ -59,6 +62,9 @@ const FleetAppDetailsForm = ({
onCancel,
onSubmit,
}: IFleetAppDetailsFormProps) => {
const gitOpsModeEnabled = useContext(AppContext).config?.gitops
.gitops_mode_enabled;
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
const [formData, setFormData] = useState<IFleetMaintainedAppFormData>({
@@ -138,10 +144,13 @@ const FleetAppDetailsForm = ({
};
const isSubmitDisabled = !formValidation.isValid;
const gitOpsModeDisabledClass = gitOpsModeEnabled
? "form-fields--disabled"
: "";
return (
<form className={baseClass} onSubmit={onSubmitForm}>
<div className={`${baseClass}__form-frame`}>
<form className={`${baseClass}`} onSubmit={onSubmitForm}>
<div className={`${baseClass}__form-frame ${gitOpsModeDisabledClass}`}>
<Card paddingSize="medium" borderRadiusSize="large">
<SoftwareOptionsSelector
formData={formData}
@@ -167,7 +176,9 @@ const FleetAppDetailsForm = ({
/>
</Card>
</div>
<div className={`${baseClass}__advanced-options-section`}>
<div
className={`${baseClass}__advanced-options-section ${gitOpsModeDisabledClass}`}
>
<RevealButton
className={`${baseClass}__accordion-title`}
isShowing={showAdvancedOptions}
@@ -199,9 +210,17 @@ const FleetAppDetailsForm = ({
)}
</div>
<div className={`${baseClass}__action-buttons`}>
<Button type="submit" variant="brand" disabled={isSubmitDisabled}>
Add software
</Button>
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
type="submit"
variant="brand"
disabled={disableChildren || isSubmitDisabled}
>
Add software
</Button>
)}
/>
<Button onClick={onCancel} variant="inverse">
Cancel
</Button>
@@ -65,4 +65,9 @@
.info-banner {
margin-top: $pad-small;
}
.form-fields--disabled {
@include disabled;
}
}
@@ -147,6 +147,10 @@ const SoftwareActionsDropdown = ({
onDeleteClick,
onEditSoftwareClick,
}: IActionsDropdownProps) => {
const config = useContext(AppContext).config;
const { gitops_mode_enabled: gitOpsModeEnabled, repository_url: repoURL } =
config?.gitops || {};
const onSelect = (action: string) => {
switch (action) {
case "download":
@@ -163,18 +167,49 @@ const SoftwareActionsDropdown = ({
}
};
let options =
installerType === "package"
? [...SOFTWARE_PACKAGE_DROPDOWN_OPTIONS]
: [...APP_STORE_APP_DROPDOWN_OPTIONS];
if (gitOpsModeEnabled) {
const tooltipContent = (
<>
{repoURL && (
<>
Manage in{" "}
<CustomLink
newTab
text="YAML"
variant="tooltip-link"
url={repoURL}
/>
<br />
</>
)}
(GitOps mode enabled)
</>
);
options = options.map((option) => {
if (option.value === "edit" || option.value === "delete") {
return {
...option,
disabled: true,
tooltipContent,
};
}
return option;
});
}
return (
<div className={`${baseClass}__actions`}>
<ActionsDropdown
className={`${baseClass}__software-actions-dropdown`}
onChange={onSelect}
placeholder="Actions"
options={
installerType === "package"
? [...SOFTWARE_PACKAGE_DROPDOWN_OPTIONS]
: [...APP_STORE_APP_DROPDOWN_OPTIONS]
}
menuAlign="right"
options={options}
/>
</div>
);
@@ -215,13 +250,13 @@ const SoftwareInstallerCard = ({
const installerType = isSoftwarePackage(softwareInstaller)
? "package"
: "vpp";
const {
isGlobalAdmin,
isGlobalMaintainer,
isTeamAdmin,
isTeamMaintainer,
} = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);
const [showEditSoftwareModal, setShowEditSoftwareModal] = useState(false);
@@ -44,31 +44,12 @@ const ApplePushCertInfo = ({
</div>
</dl>
<div className={`${baseClass}__apns-button-wrap`}>
<GitOpsModeTooltipWrapper
tipOffset={8}
renderChildren={(disableChildren) => (
<Button
variant="inverse"
onClick={onClickTurnOff}
disabled={disableChildren}
>
Turn off MDM
</Button>
)}
/>
<GitOpsModeTooltipWrapper
tipOffset={8}
renderChildren={(disableChildren) => (
<Button
className="save-loading"
variant="brand"
onClick={onClickRenew}
disabled={disableChildren}
>
Renew certificate
</Button>
)}
/>
<Button variant="inverse" onClick={onClickTurnOff}>
Turn off MDM
</Button>
<Button className="save-loading" variant="brand" onClick={onClickRenew}>
Renew certificate
</Button>
</div>
</>
);
@@ -192,22 +192,27 @@ const Advanced = ({
<p className={`${baseClass}__section-description`}>
Most users do not need to modify these options.
</p>
<div
className={`form ${
gitOpsModeEnabled ? "disabled-by-gitops-mode" : ""
}`}
>
<div className="form">
{appConfig.mdm.enabled_and_configured && (
<InputField
label="Apple MDM server URL"
onChange={onInputChange}
onBlur={onInputBlur}
name="mdmAppleServerURL"
value={mdmAppleServerURL}
parseTarget
error={formErrors.mdmAppleServerURL}
tooltip="Update this URL if you're self-hosting Fleet and you want your hosts to talk to this URL for MDM features. If not configured, hosts will use the base URL of the Fleet instance."
helpText="If this URL changes and hosts already have MDM turned on, the end users will have to turn MDM off and back on to use MDM features."
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<InputField
disabled={disableChildren}
label="Apple MDM server URL"
onChange={onInputChange}
onBlur={onInputBlur}
name="mdmAppleServerURL"
value={mdmAppleServerURL}
parseTarget
error={formErrors.mdmAppleServerURL}
tooltip={
!disableChildren &&
"Update this URL if you're self-hosting Fleet and you want your hosts to talk to this URL for MDM features. If not configured, hosts will use the base URL of the Fleet instance."
}
helpText="If this URL changes and hosts already have MDM turned on, the end users will have to turn MDM off and back on to use MDM features."
/>
)}
/>
)}
<InputField
@@ -264,157 +269,222 @@ const Advanced = ({
>
Enable STARTTLS
</Checkbox>
<Checkbox
onChange={onInputChange}
name="enableHostExpiry"
value={enableHostExpiry}
parseTarget
tooltipContent={
<>
When enabled, allows automatic cleanup of
<br />
hosts that have not communicated with Fleet in
<br />
the number of days specified in the{" "}
<strong>
Host expiry
<br />
window
</strong>{" "}
setting.{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
}
>
Host expiry
</Checkbox>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="enableHostExpiry"
value={enableHostExpiry}
parseTarget
tooltipContent={
!disableChildren && (
<>
When enabled, allows automatic cleanup of
<br />
hosts that have not communicated with Fleet in
<br />
the number of days specified in the{" "}
<strong>
Host expiry
<br />
window
</strong>{" "}
setting.{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
)
}
>
Host expiry
</Checkbox>
)}
/>
{enableHostExpiry && (
<InputField
label="Host expiry window"
type="number"
onChange={onInputChange}
name="hostExpiryWindow"
value={hostExpiryWindow}
parseTarget
error={formErrors.hostExpiryWindow}
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<InputField
disabled={disableChildren}
label="Host expiry window"
type="number"
onChange={onInputChange}
name="hostExpiryWindow"
value={hostExpiryWindow}
parseTarget
error={formErrors.hostExpiryWindow}
/>
)}
/>
)}
<Checkbox
onChange={onInputChange}
name="deleteActivities"
value={deleteActivities}
parseTarget
tooltipContent={
<>
When enabled, allows automatic cleanup of audit logs older
than the number of days specified in the{" "}
<em>Audit log retention window</em> setting.
<em>
(Default: <strong>Off</strong>)
</em>
</>
}
>
Delete activities
</Checkbox>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="deleteActivities"
value={deleteActivities}
parseTarget
tooltipContent={
!disableChildren && (
<>
When enabled, allows automatic cleanup of audit logs
older than the number of days specified in the{" "}
<em>Audit log retention window</em> setting.
<em>
(Default: <strong>Off</strong>)
</em>
</>
)
}
>
Delete activities
</Checkbox>
)}
/>
{deleteActivities && (
<Dropdown
searchable={false}
options={activityExpiryWindowOptions}
onChange={onInputChange}
placeholder="Select"
value={activityExpiryWindow}
label="Max activity age"
name="activityExpiryWindow"
parseTarget
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Dropdown
disabled={disableChildren}
searchable={false}
options={activityExpiryWindowOptions}
onChange={onInputChange}
placeholder="Select"
value={activityExpiryWindow}
label="Max activity age"
name="activityExpiryWindow"
parseTarget
/>
)}
/>
)}
<Checkbox
onChange={onInputChange}
name="disableLiveQuery"
value={disableLiveQuery}
parseTarget
tooltipContent={
<>
When enabled, disables the ability to run live queries <br />
(ad hoc queries executed via the UI or fleetctl).{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
}
>
Disable live queries
</Checkbox>
<Checkbox
onChange={onInputChange}
name="disableScripts"
value={disableScripts}
parseTarget
tooltipContent={
<>
Disabling script execution will block access to run scripts.
<br />
Scripts may still be added and removed in the UI and API.
<br />
<em>
(Default: <b>Off</b>)
</em>
</>
}
helpText="Features that run scripts under-the-hood (e.g. software install, lock/wipe) will still be available."
>
Disable script execution features
</Checkbox>
<Checkbox
onChange={onInputChange}
name="disableAIFeatures"
value={disableAIFeatures}
parseTarget
tooltipContent={
<>
When enabled, disables AI features such as pre-filling forms
<br />
with descriptions generated by a large language model
<br />
(LLM).{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
}
helpText="If enabled, only policy queries (SQL) are sent to the LLM. Fleet doesnt use this data to train models."
>
Disable generative AI features
</Checkbox>
<Checkbox
onChange={onInputChange}
name="disableQueryReports"
value={disableQueryReports}
parseTarget
tooltipContent={
<>
<>
Disabling query reports will decrease database usage, <br />
but will prevent you from accessing query results in
<br />
Fleet and will delete existing reports. This can also be{" "}
<br />
disabled on a per-query basis by enabling &quot;Discard{" "}
<br />
data&quot;.{" "}
<em>
(Default: <b>Off</b>)
</em>
</>
</>
}
helpText="Enabling this setting will delete all existing query reports in Fleet."
>
Disable query reports
</Checkbox>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="disableLiveQuery"
value={disableLiveQuery}
parseTarget
tooltipContent={
!disableChildren && (
<>
When enabled, disables the ability to run live queries{" "}
<br />
(ad hoc queries executed via the UI or fleetctl).{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
)
}
>
Disable live queries
</Checkbox>
)}
/>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="disableScripts"
value={disableScripts}
parseTarget
tooltipContent={
!disableChildren && (
<>
Disabling script execution will block access to run
scripts.
<br />
Scripts may still be added and removed in the UI and
API.
<br />
<em>
(Default: <b>Off</b>)
</em>
</>
)
}
helpText="Features that run scripts under-the-hood (e.g. software install, lock/wipe) will still be available."
>
Disable script execution features
</Checkbox>
)}
/>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="disableAIFeatures"
value={disableAIFeatures}
parseTarget
tooltipContent={
!disableChildren && (
<>
When enabled, disables AI features such as pre-filling
forms
<br />
with descriptions generated by a large language model
<br />
(LLM).{" "}
<em>
(Default: <strong>Off</strong>)
</em>
</>
)
}
helpText="If enabled, only policy queries (SQL) are sent to the LLM. Fleet doesnt use this data to train models."
>
Disable generative AI features
</Checkbox>
)}
/>
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<Checkbox
disabled={disableChildren}
onChange={onInputChange}
name="disableQueryReports"
value={disableQueryReports}
parseTarget
tooltipContent={
!disableChildren && (
<>
<>
Disabling query reports will decrease database usage,{" "}
<br />
but will prevent you from accessing query results in
<br />
Fleet and will delete existing reports. This can also
be <br />
disabled on a per-query basis by enabling
&quot;Discard <br />
data&quot;.{" "}
<em>
(Default: <b>Off</b>)
</em>
</>
</>
)
}
helpText="Enabling this setting will delete all existing query reports in Fleet."
>
Disable query reports
</Checkbox>
)}
/>
</div>
<GitOpsModeTooltipWrapper
tipOffset={-8}
@@ -414,7 +414,6 @@ const UsersPage = ({ location, router }: ITeamSubnavProps): JSX.Element => {
? toggleAddUserModal
: toggleCreateUserModal,
hideButton: userIds.length === 0 && searchString === "",
gitOpsModeCompatible: true,
}}
onQueryChange={({ searchQuery }) => setSearchString(searchQuery)}
inputPlaceHolder="Search"
@@ -168,20 +168,12 @@ const generateColumnConfigs = (
disableSortBy: true,
accessor: "actions",
Cell: (cellProps: IActionsDropdownProps) => (
<GitOpsModeTooltipWrapper
position="left"
renderChildren={(disableChildren) => (
<div className={disableChildren ? "disabled-by-gitops-mode" : ""}>
<ActionsDropdown
options={cellProps.cell.value}
onChange={(value: string) =>
actionSelectHandler(value, cellProps.row.original)
}
placeholder="Actions"
disabled={disableChildren}
/>
</div>
)}
<ActionsDropdown
options={cellProps.cell.value}
onChange={(value: string) =>
actionSelectHandler(value, cellProps.row.original)
}
placeholder="Actions"
/>
),
},