Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion frontend/interfaces/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ export enum ActivityType {
AddedMicrosoftEntraTenant = "added_microsoft_entra_tenant",
DeletedMicrosoftEntraTenant = "deleted_microsoft_entra_tenant",
ClearedPasscode = "cleared_passcode",
EnabledManagedLocalAccount = "enabled_managed_local_account",
DisabledManagedLocalAccount = "disabled_managed_local_account",
ReadManagedLocalAccount = "read_managed_local_account",
CreatedManagedLocalAccount = "created_managed_local_account",
RotatedManagedLocalAccountPassword = "rotated_managed_local_account_password",
}

/** This is a subset of ActivityType that are shown only for the host past activities */
Expand All @@ -182,7 +187,10 @@ export type IHostPastActivityType =
| ActivityType.CanceledUninstallSoftware
| ActivityType.InstalledCertificate
| ActivityType.ResentCertificate
| ActivityType.ClearedPasscode;
| ActivityType.ClearedPasscode
| ActivityType.ReadManagedLocalAccount
| ActivityType.CreatedManagedLocalAccount
| ActivityType.RotatedManagedLocalAccountPassword;

/** This is a subset of ActivityType that are shown only for the host upcoming activities */
export type IHostUpcomingActivityType =
Expand Down Expand Up @@ -462,4 +470,11 @@ export const ACTIVITY_TYPE_TO_FILTER_LABEL: Record<ActivityType, string> = {
[ActivityType.InstalledCertificate]: "Installed certificate",
[ActivityType.EditedEnrollSecrets]: "Edited enroll secrets",
[ActivityType.ClearedPasscode]: "Cleared passcode",
[ActivityType.EnabledManagedLocalAccount]: "Turned on managed local account",
[ActivityType.DisabledManagedLocalAccount]:
"Turned off managed local account",
[ActivityType.ReadManagedLocalAccount]: "Viewed managed account",
[ActivityType.CreatedManagedLocalAccount]: "Created managed account",
[ActivityType.RotatedManagedLocalAccountPassword]:
"Rotated managed account password",
};
1 change: 1 addition & 0 deletions frontend/interfaces/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface IMdmConfig {
macos_manual_agent_install: boolean | null;
require_all_software_macos: boolean | null;
lock_end_user_info: boolean | null;
enable_managed_local_account?: boolean;
};
macos_migration: IMacOsMigrationSettings;
windows_updates: {
Expand Down
8 changes: 8 additions & 0 deletions frontend/interfaces/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,14 @@ export interface IHostRecoveryLockPasswordResponse {
};
}

export interface IHostManagedAccountPasswordResponse {
host_id: number;
managed_account_password: {
password: string;
updated_at: string;
};
}

export interface IHostIssues {
total_issues_count: number;
critical_vulnerabilities_count?: number; // Premium
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,63 @@ const TAGGED_TEMPLATES = {
</>
);
},
enabledManagedLocalAccount: (activity: IActivity) => {
return (
<>
{" "}
turned on managed local account for{" "}
{activity.details?.team_name ? (
<>
<b>{activity.details.team_name}</b> fleet.
</>
) : (
"hosts with no fleet."
)}
</>
);
},
disabledManagedLocalAccount: (activity: IActivity) => {
return (
<>
{" "}
turned off managed local account for{" "}
{activity.details?.team_name ? (
<>
<b>{activity.details.team_name}</b> fleet.
</>
) : (
"hosts with no fleet."
)}
</>
);
},
readManagedLocalAccount: (activity: IActivity) => {
return (
<>
{" "}
viewed the managed account for{" "}
<b>{activity.details?.host_display_name}</b>.
</>
);
},
createdManagedLocalAccount: (activity: IActivity) => {
return (
<>
{" "}
created the managed account for{" "}
<b>{activity.details?.host_display_name}</b>.
</>
);
},
rotatedManagedLocalAccountPassword: (activity: IActivity) => {
return (
<>
{" "}
rotated the managed account password for{" "}
<b>{activity.details?.host_display_name}</b>.
</>
);
},
createdAppleOSProfile: (activity: IActivity, isPremiumTier: boolean) => {
const profileName = activity.details?.profile_name;
return (
Expand Down Expand Up @@ -1897,6 +1954,21 @@ const getDetail = (activity: IActivity, isPremiumTier: boolean) => {
case ActivityType.RotatedHostRecoveryLockPassword: {
return TAGGED_TEMPLATES.rotatedHostRecoveryLockPassword(activity);
}
case ActivityType.EnabledManagedLocalAccount: {
return TAGGED_TEMPLATES.enabledManagedLocalAccount(activity);
}
case ActivityType.DisabledManagedLocalAccount: {
return TAGGED_TEMPLATES.disabledManagedLocalAccount(activity);
}
case ActivityType.ReadManagedLocalAccount: {
return TAGGED_TEMPLATES.readManagedLocalAccount(activity);
}
case ActivityType.CreatedManagedLocalAccount: {
return TAGGED_TEMPLATES.createdManagedLocalAccount(activity);
}
case ActivityType.RotatedManagedLocalAccountPassword: {
return TAGGED_TEMPLATES.rotatedManagedLocalAccountPassword(activity);
}
case ActivityType.CreatedAppleOSProfile: {
return TAGGED_TEMPLATES.createdAppleOSProfile(activity, isPremiumTier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface IHostActionsDropdownProps {
isRecoveryLockPasswordEnabled?: boolean;
diskEncryptionProfileStatus?: string;
recoveryLockPasswordAvailable?: boolean;
isManagedLocalAccountEnabled?: boolean;
}

const HostActionsDropdown = ({
Expand All @@ -42,6 +43,7 @@ const HostActionsDropdown = ({
isRecoveryLockPasswordEnabled = false,
diskEncryptionProfileStatus,
recoveryLockPasswordAvailable = false,
isManagedLocalAccountEnabled = false,
}: IHostActionsDropdownProps) => {
const {
isPremiumTier = false,
Expand Down Expand Up @@ -96,6 +98,7 @@ const HostActionsDropdown = ({
isRecoveryLockPasswordEnabled,
diskEncryptionProfileStatus,
recoveryLockPasswordAvailable,
isManagedLocalAccountEnabled,
});

// No options to render. Exit early
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const DEFAULT_OPTIONS = [
value: "recoveryLockPassword",
disabled: false,
},
{
label: "Show managed account",
value: "managedAccount",
disabled: false,
},
{
label: "Turn off MDM",
value: "mdmOff",
Expand Down Expand Up @@ -108,6 +113,7 @@ interface IHostActionConfigOptions {
isRecoveryLockPasswordEnabled: boolean;
diskEncryptionProfileStatus: string | undefined;
recoveryLockPasswordAvailable: boolean;
isManagedLocalAccountEnabled: boolean;
}

const canTransferTeam = (config: IHostActionConfigOptions) => {
Expand Down Expand Up @@ -317,6 +323,21 @@ const canShowRecoveryLockPassword = (config: IHostActionConfigOptions) => {
return isRecoveryLockPasswordEnabled;
};

const canShowManagedAccount = (config: IHostActionConfigOptions) => {
const {
isPremiumTier,
isConnectedToFleetMdm,
isEnrolledInMdm,
hostPlatform,
isManagedLocalAccountEnabled,
} = config;
if (!isPremiumTier) return false;
if (hostPlatform !== "darwin") return false;
if (!isConnectedToFleetMdm) return false;
if (!isEnrolledInMdm) return false;
return isManagedLocalAccountEnabled;
};

const canClearPasscode = (config: IHostActionConfigOptions) => {
if (!config.isPremiumTier) {
return false;
Expand Down Expand Up @@ -398,6 +419,10 @@ const removeUnavailableOptions = (
);
}

if (!canShowManagedAccount(config)) {
options = options.filter((option) => option.value !== "managedAccount");
}

if (!canClearPasscode(config)) {
options = options.filter((option) => option.value !== "clearPasscode");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ import DeleteHostModal from "../../components/DeleteHostModal";
import UnenrollMdmModal from "./modals/UnenrollMdmModal";
import DiskEncryptionKeyModal from "./modals/DiskEncryptionKeyModal";
import RecoveryLockPasswordModal from "./modals/RecoveryLockPasswordModal";
import ManagedAccountModal from "./modals/ManagedAccountModal";
import HostActionsDropdown from "./HostActionsDropdown/HostActionsDropdown";
import OSSettingsModal from "../OSSettingsModal";
import BootstrapPackageModal from "./modals/BootstrapPackageModal";
Expand Down Expand Up @@ -226,6 +227,7 @@ const HostDetailsPage = ({
showRecoveryLockPasswordModal,
setShowRecoveryLockPasswordModal,
] = useState(false);
const [showManagedAccountModal, setShowManagedAccountModal] = useState(false);
const [showBootstrapPackageModal, setShowBootstrapPackageModal] = useState(
false
);
Expand Down Expand Up @@ -948,6 +950,9 @@ const HostDetailsPage = ({
case "recoveryLockPassword":
setShowRecoveryLockPasswordModal(true);
break;
case "managedAccount":
setShowManagedAccountModal(true);
break;
case "mdmOff":
toggleUnenrollMdmModal();
break;
Expand Down Expand Up @@ -1009,6 +1014,9 @@ const HostDetailsPage = ({
host.mdm.os_settings?.recovery_lock_password?.password_available ??
false
}
isManagedLocalAccountEnabled={
mdmConfig?.setup_experience?.enable_managed_local_account ?? false
}
/>
);
};
Expand Down Expand Up @@ -1611,6 +1619,12 @@ const HostDetailsPage = ({
onCancel={() => setShowRecoveryLockPasswordModal(false)}
/>
)}
{showManagedAccountModal && host && (
<ManagedAccountModal
hostId={host.id}
onCancel={() => setShowManagedAccountModal(false)}
/>
)}
{showBootstrapPackageModal &&
bootstrapPackageData.details &&
bootstrapPackageData.name && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from "react";
import { useQuery } from "react-query";

import { IHostManagedAccountPasswordResponse } from "interfaces/host";
import hostAPI from "services/entities/hosts";

import Modal from "components/Modal";
import Button from "components/buttons/Button";
import InputFieldHiddenContent from "components/forms/fields/InputFieldHiddenContent";
import DataError from "components/DataError";
import Spinner from "components/Spinner";
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";

const baseClass = "managed-account-modal";

interface IManagedAccountModalProps {
hostId: number;
onCancel: () => void;
}

const ManagedAccountModal = ({
hostId,
onCancel,
}: IManagedAccountModalProps) => {
const {
data: managedAccountData,
error: managedAccountError,
isLoading,
} = useQuery<
IHostManagedAccountPasswordResponse,
unknown,
IHostManagedAccountPasswordResponse["managed_account_password"]
>(
["hostManagedAccountPassword", hostId],
() => hostAPI.getManagedAccountPassword(hostId),
{
...DEFAULT_USE_QUERY_OPTIONS,
select: (data) => data.managed_account_password,
// prevent caching this sensitive string
cacheTime: 0,
}
);

return (
<Modal
title="Managed account"
onExit={onCancel}
onEnter={onCancel}
className={baseClass}
>
{isLoading && <Spinner />}
{managedAccountError ? (
<DataError />
) : (
!isLoading && (
<>
<div className={`${baseClass}__username`}>
<span className={`${baseClass}__label`}>Username</span>
<span className={`${baseClass}__value`}>_fleetadmin</span>
</div>
<InputFieldHiddenContent
value={managedAccountData?.password ?? ""}
name="Password"
/>
<div className="modal-cta-wrap">
<Button onClick={onCancel}>Close</Button>
</div>
</>
)
)}
</Modal>
);
};

export default ManagedAccountModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.managed-account-modal {
&__username {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}

&__label {
font-size: $x-small;
font-weight: $bold;
color: $core-fleet-black;
}

&__value {
font-size: $x-small;
color: $ui-fleet-black-75;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ManagedAccountModal";
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import CanceledUninstallSoftwareActivtyItem from "./ActivityItems/CanceledUninst
import InstalledCertificateActivityItem from "./ActivityItems/InstalledCertificateActivityItem";
import ResentCertificateActivityItem from "./ActivityItems/ResentCertificateActivityItem";
import ClearedPasscodeActivityItem from "./ActivityItems/ClearedPasscodeActivityItem";
import ReadManagedLocalAccountActivityItem from "./ActivityItems/ReadManagedLocalAccountActivityItem/ReadManagedLocalAccountActivityItem";
import CreatedManagedLocalAccountActivityItem from "./ActivityItems/CreatedManagedLocalAccountActivityItem/CreatedManagedLocalAccountActivityItem";
import RotatedManagedLocalAccountPasswordActivityItem from "./ActivityItems/RotatedManagedLocalAccountPasswordActivityItem/RotatedManagedLocalAccountPasswordActivityItem";

/** The component props that all host activity items must adhere to */
export interface IHostActivityItemComponentProps {
Expand Down Expand Up @@ -70,6 +73,9 @@ export const pastActivityComponentMap: Record<
[ActivityType.InstalledCertificate]: InstalledCertificateActivityItem,
[ActivityType.ResentCertificate]: ResentCertificateActivityItem,
[ActivityType.ClearedPasscode]: ClearedPasscodeActivityItem,
[ActivityType.ReadManagedLocalAccount]: ReadManagedLocalAccountActivityItem,
[ActivityType.CreatedManagedLocalAccount]: CreatedManagedLocalAccountActivityItem,
[ActivityType.RotatedManagedLocalAccountPassword]: RotatedManagedLocalAccountPasswordActivityItem,
};

export const upcomingActivityComponentMap: Record<
Expand Down
Loading
Loading