{endUserAuthEnabled && (
-
- To enable, first turn on{" "}
-
- .
-
- ) : undefined
- }
- disableTooltip={!!isMacMdmEnabledAndConfigured}
- underline={false}
- position="left"
- showArrow
+
Lock end user info
-
+
)}
diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx
index e29b25da84..5580884efe 100644
--- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx
+++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/LocalAccountSection/LocalAccountSection.tsx
@@ -1,14 +1,11 @@
import React from "react";
-import PATHS from "router/paths";
-
-import CustomLink from "components/CustomLink";
-import TooltipWrapper from "components/TooltipWrapper";
import Radio from "components/forms/fields/Radio";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { EndUserLocalAccountType } from "interfaces/mdm";
import ManagedAccountCheckbox from "../ManagedAccountCheckbox";
+import TurnOnMdmTooltipWrapper from "../TurnOnMdmTooltipWrapper";
import { IUsersFormData } from "../../UsersForm";
const baseClass = "local-account-section";
@@ -38,24 +35,9 @@ const LocalAccountSection = ({
localAccountType !== EndUserLocalAccountType.ADMIN;
return (
-
- To enable, first turn on{" "}
-
- .
-
- ) : undefined
- }
- disableTooltip={isMacMdmEnabledAndConfigured}
- underline={false}
- position="left"
- showArrow
+
-
+
);
};
diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx
new file mode 100644
index 0000000000..dc3baaaa5c
--- /dev/null
+++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/TurnOnMdmTooltipWrapper.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+
+import PATHS from "router/paths";
+
+import CustomLink from "components/CustomLink";
+import TooltipWrapper from "components/TooltipWrapper";
+
+const MDM_BY_PLATFORM = {
+ apple: { url: PATHS.ADMIN_INTEGRATIONS_MDM_APPLE, text: "Apple MDM" },
+ windows: { url: PATHS.ADMIN_INTEGRATIONS_MDM_WINDOWS, text: "Windows MDM" },
+} as const;
+
+interface ITurnOnMdmTooltipWrapperProps {
+ platform: keyof typeof MDM_BY_PLATFORM;
+ isMdmEnabledAndConfigured: boolean;
+ children: React.ReactNode;
+}
+
+/** Explains that a control is disabled because the platform's MDM isn't turned on yet, and links to the page that turns it on. */
+const TurnOnMdmTooltipWrapper = ({
+ platform,
+ isMdmEnabledAndConfigured,
+ children,
+}: ITurnOnMdmTooltipWrapperProps) => {
+ const { url, text } = MDM_BY_PLATFORM[platform];
+
+ return (
+
+ To enable, first turn on{" "}
+ .
+
+ ) : undefined
+ }
+ disableTooltip={isMdmEnabledAndConfigured}
+ underline={false}
+ position="left"
+ showArrow
+ >
+ {children}
+
+ );
+};
+
+export default TurnOnMdmTooltipWrapper;
diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts
new file mode 100644
index 0000000000..8f18c8b88a
--- /dev/null
+++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/TurnOnMdmTooltipWrapper/index.ts
@@ -0,0 +1 @@
+export { default } from "./TurnOnMdmTooltipWrapper";
diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx
index 6cde72508c..41c5ece1c9 100644
--- a/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx
+++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/components/WindowsAccountSection/WindowsAccountSection.tsx
@@ -5,18 +5,21 @@ import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
import ManagedAccountCheckbox from "../ManagedAccountCheckbox";
+import TurnOnMdmTooltipWrapper from "../TurnOnMdmTooltipWrapper";
const baseClass = "windows-account-section";
interface IWindowsAccountSectionProps {
enableManagedLocalAccount: boolean;
onEnableManagedLocalAccountChange: (value: boolean) => void;
+ isWindowsMdmEnabledAndConfigured: boolean;
}
-/** Windows tab of the Users card. The tab is only rendered when Windows MDM is configured, so unlike the macOS section there is no "turn on MDM first" state to handle here. */
+/** Windows tab of the Users card. The managed account checkbox mirrors the macOS tab, including the disabled state that points admins at Windows MDM when it isn't turned on yet. */
const WindowsAccountSection = ({
enableManagedLocalAccount,
onEnableManagedLocalAccountChange,
+ isWindowsMdmEnabledAndConfigured,
}: IWindowsAccountSectionProps) => {
return (
@@ -32,18 +35,23 @@ const WindowsAccountSection = ({
Managed account
- (
-
- )}
- />
+
+ (
+
+ )}
+ />
+
);
diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss
index 1659d8f801..7a4e851b68 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss
+++ b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/_styles.scss
@@ -13,3 +13,9 @@
min-width: 200px;
}
}
+
+// Left-align error tooltip.
+.host-actions-dropdown__managed-account-error {
+ display: block;
+ text-align: left;
+}
diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx
index 5a15dcb728..fe1575bf19 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/HostActionsDropdown/helpers.tsx
@@ -857,7 +857,11 @@ const modifyOptions = (
);
} else if (managedAccountStatus === "failed") {
// The reason the host reported is the actionable part, so prefer it over generic copy.
- managedAccountOption.tooltipContent = managedAccountDetail || (
+ managedAccountOption.tooltipContent = managedAccountDetail ? (
+
+ {managedAccountDetail}
+
+ ) : (
<>
The managed account failed to be
diff --git a/orbit/pkg/managedaccount/managedaccount_windows.go b/orbit/pkg/managedaccount/managedaccount_windows.go
index b1e5439a9b..345bf3683d 100644
--- a/orbit/pkg/managedaccount/managedaccount_windows.go
+++ b/orbit/pkg/managedaccount/managedaccount_windows.go
@@ -128,7 +128,7 @@ func ensureUser(username, password string) error {
0, // parm_err
)
if ret != 0 {
- return accountError(fmt.Sprintf("resetting password for %s", username), ret, len(password))
+ return accountError(fmt.Sprintf("Resetting password for %s", username), ret, len(password))
}
// Resetting the password is not enough to make the account usable again. If it was disabled, locked out, or had
// its never-expire flag removed after we created it, Fleet would escrow a password that cannot actually log in.
@@ -154,7 +154,7 @@ func ensureUser(username, password string) error {
0, // parm_err
)
if ret != 0 {
- return accountError(fmt.Sprintf("creating %s", username), ret, len(password))
+ return accountError(fmt.Sprintf("Creating %s", username), ret, len(password))
}
return nil
}
@@ -166,8 +166,8 @@ func ensureUser(username, password string) error {
func accountError(op string, ret uintptr, passwordLen int) error {
if ret == nerrPasswordTooShort || ret == errorPasswordRestriction {
return fmt.Errorf(
- "%s: this device's password policy rejected the generated %d-character password; "+
- "check the minimum password length and any custom password filter on the host",
+ "%s. This device's password policy rejected the generated %d-character password. "+
+ "Check any custom password filter on the host.",
op, passwordLen)
}
return fmt.Errorf("%s: %w", op, windows.Errno(ret))