Skip to content

Commit

Permalink
update UI to add support for mac ddm profiles (#17730)
Browse files Browse the repository at this point in the history
relates to #17416

update UI to support new DDM profile types. this includes:

- updating Custom settings page
- updating the os settings modal  

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Manual QA for all new/changed functionality
  • Loading branch information
ghernandez345 authored Mar 25, 2024
1 parent 577d5c2 commit d3c8438
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 80 deletions.
1 change: 1 addition & 0 deletions changes/issue-17416-update-ui-to-support-ddm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- update UI to support macos DDM profiles.
2 changes: 1 addition & 1 deletion frontend/__mocks__/hostMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const DEFAULT_HOST_PROFILE_MOCK: IHostMdmProfile = {
detail: "This is verified",
};

export const createMockHostMacMdmProfile = (
export const createMockHostMdmProfile = (
overrides?: Partial<IHostMdmProfile>
): IHostMdmProfile => {
return { ...DEFAULT_HOST_PROFILE_MOCK, ...overrides };
Expand Down
7 changes: 6 additions & 1 deletion frontend/interfaces/mdm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export interface IMdmProfile {
}

export type MdmProfileStatus = "verified" | "verifying" | "pending" | "failed";
export type MdmDDMProfileStatus =
| "success"
| "pending"
| "failed"
| "acknowledged";

export type ProfileOperationType = "remove" | "install";

Expand All @@ -89,7 +94,7 @@ export interface IHostMdmProfile {
name: string;
operation_type: ProfileOperationType | null;
platform: ProfilePlatform;
status: MdmProfileStatus;
status: MdmProfileStatus | MdmDDMProfileStatus;
detail: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const AGGREGATE_STATUS_DISPLAY_OPTIONS: IAggregateDisplayOption[] = [
text: "Verified",
iconName: "success",
tooltipText:
"These hosts applied all OS settings. Fleet verified with osquery.",
"These hosts applied all OS settings. Fleet verified with osquery. " +
"Declaration profiles are verified with DDM.",
},
{
value: "verifying",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
flex-direction: column;
border-radius: $border-radius;
border: 1px solid $ui-fleet-black-10;
overflow-y: scroll;
overflow-y: auto;

.loading-spinner {
margin: 69.5px auto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import FileSaver from "file-saver";
import classnames from "classnames";

import { IMdmProfile } from "interfaces/mdm";
import mdmAPI from "services/entities/mdm";
import mdmAPI, { isDDMProfile } from "services/entities/mdm";

import Button from "components/buttons/Button";
import Graphic from "components/Graphic";
Expand All @@ -29,11 +29,17 @@ const LabelCount = ({
interface IProfileDetailsProps {
platform: string;
createdAt: string;
isDDM?: boolean;
}

const ProfileDetails = ({ platform, createdAt }: IProfileDetailsProps) => {
const ProfileDetails = ({
platform,
createdAt,
isDDM,
}: IProfileDetailsProps) => {
const getPlatformName = () => {
return platform === "darwin" ? "macOS" : "Windows";
if (platform === "windows") return "Windows";
return isDDM ? "macOS (declaration)" : "macOS";
};

return (
Expand Down Expand Up @@ -81,7 +87,11 @@ const ProfileListItem = ({
<div className={`${subClass}__info`}>
<span className={`${subClass}__title`}>{name}</span>
<div className={`${subClass}__details`}>
<ProfileDetails platform={platform} createdAt={created_at} />
<ProfileDetails
platform={platform}
createdAt={created_at}
isDDM={isDDMProfile(profile)}
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
import Graphic from "components/Graphic";

const ALLOWED_FILE_TYPES_MESSAGE =
"Configuration profile (.mobileconfig for macOS or .xml for Windows)";
"Configuration profile (.mobileconfig and .json for macOS or .xml for Windows)";

const ProfileGraphic = ({
baseClass,
Expand Down
26 changes: 25 additions & 1 deletion frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { QueryContext } from "context/query";
import { NotificationContext } from "context/notification";

import activitiesAPI, {
IActivitiesResponse,
IPastActivitiesResponse,
IUpcomingActivitiesResponse,
} from "services/entities/activities";
Expand Down Expand Up @@ -53,6 +52,7 @@ import {
HOST_ABOUT_DATA,
HOST_OSQUERY_DATA,
} from "utilities/constants";
import { createMockHostMdmProfile } from "__mocks__/hostMock";

import Spinner from "components/Spinner";
import TabsWrapper from "components/TabsWrapper";
Expand Down Expand Up @@ -753,6 +753,30 @@ const HostDetailsPage = ({
name: host?.mdm.macos_setup?.bootstrap_package_name,
};

// TODO: Remove this when API is ready
if (!host.mdm.profiles) {
host.mdm.profiles = [];
} else {
host.mdm.profiles = [
createMockHostMdmProfile({
name: "test.json",
status: "success",
}),
createMockHostMdmProfile({
name: "test2.json",
status: "pending",
}),
createMockHostMdmProfile({
name: "test3.json",
status: "failed",
}),
createMockHostMdmProfile({
name: "test4.json",
status: "acknowledged",
}),
];
}

return (
<MainContent className={baseClass}>
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,35 @@ type OperationTypeOption = Record<

type ProfileDisplayConfig = Record<ProfileOperationType, OperationTypeOption>;

const MAC_PROFILE_VERIFIED_DISPLAY_CONFIG: ProfileDisplayOption = {
statusText: "Verified",
iconName: "success",
tooltip: (innerProps) =>
innerProps.isDiskEncryptionProfile
? "The host turned disk encryption on and sent the key to Fleet. " +
"Fleet verified with osquery."
: "The host applied the setting. Fleet verified with osquery. " +
"Declaration profiles are verified with DDM.",
} as const;

const MAC_PROFILE_VERIFYING_DISPLAY_CONFIG: ProfileDisplayOption = {
statusText: "Verifying",
iconName: "success-outline",
tooltip: (innerProps) =>
innerProps.isDiskEncryptionProfile
? "The host acknowledged the MDM command to turn on disk encryption. " +
"Fleet is verifying with osquery and retrieving the disk encryption key. " +
"This may take up to one hour."
: "The host acknowledged the MDM command to apply the setting. Fleet is " +
"verifying with osquery.",
} as const;

export const PROFILE_DISPLAY_CONFIG: ProfileDisplayConfig = {
install: {
verified: {
statusText: "Verified",
iconName: "success",
tooltip: (innerProps) =>
innerProps.isDiskEncryptionProfile
? "The host turned disk encryption on and sent the key to Fleet. " +
"Fleet verified with osquery."
: "The host applied the setting. Fleet verified with osquery.",
},
verifying: {
statusText: "Verifying",
iconName: "success-outline",
tooltip: (innerProps) =>
innerProps.isDiskEncryptionProfile
? "The host acknowledged the MDM command to turn on disk encryption. " +
"Fleet is verifying with osquery and retrieving the disk encryption key. " +
"This may take up to one hour."
: "The host acknowledged the MDM command to apply the setting. Fleet is " +
"verifying with osquery.",
},
verified: MAC_PROFILE_VERIFIED_DISPLAY_CONFIG,
success: MAC_PROFILE_VERIFIED_DISPLAY_CONFIG,
verifying: MAC_PROFILE_VERIFYING_DISPLAY_CONFIG,
acknowledged: MAC_PROFILE_VERIFYING_DISPLAY_CONFIG,
pending: {
statusText: "Enforcing (pending)",
iconName: "pending-outline",
Expand Down Expand Up @@ -79,6 +86,8 @@ export const PROFILE_DISPLAY_CONFIG: ProfileDisplayConfig = {
action_required: null, // should not be reached
verified: null, // should not be reached
verifying: null, // should not be reached
success: null, // should not be reached
acknowledged: null, // should not be reached
failed: {
statusText: "Failed",
iconName: "error",
Expand All @@ -89,7 +98,8 @@ export const PROFILE_DISPLAY_CONFIG: ProfileDisplayConfig = {

type WindowsDiskEncryptionDisplayConfig = Omit<
OperationTypeOption,
"action_required"
// windows disk encryption does not have these states
"action_required" | "success" | "acknowledged"
>;

export const WINDOWS_DISK_ENCRYPTION_DISPLAY_CONFIG: WindowsDiskEncryptionDisplayConfig = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from "react";
import TableContainer from "components/TableContainer";

import tableHeaders, { ITableRowOsSettings } from "./OSSettingsTableConfig";
import tableHeaders, {
IHostMdmProfileWithAddedStatus,
} from "./OSSettingsTableConfig";

const baseClass = "os-settings-table";

interface IOSSettingsTableProps {
tableData?: ITableRowOsSettings[];
tableData?: IHostMdmProfileWithAddedStatus[];
}

const OSSettingsTable = ({ tableData }: IOSSettingsTableProps) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,44 @@
import TextCell from "components/TableContainer/DataTable/TextCell";
import React from "react";
import { Column } from "react-table";

import { IStringCellProps } from "interfaces/datatable_config";
import { IHostMdmData } from "interfaces/host";
import {
FLEET_FILEVAULT_PROFILE_DISPLAY_NAME,
// FLEET_FILEVAULT_PROFILE_IDENTIFIER,
IHostMdmProfile,
MdmDDMProfileStatus,
MdmProfileStatus,
ProfilePlatform,
isWindowsDiskEncryptionStatus,
} from "interfaces/mdm";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";

import TextCell from "components/TableContainer/DataTable/TextCell";
import TooltipTruncatedTextCell from "components/TableContainer/DataTable/TooltipTruncatedTextCell";

import OSSettingStatusCell from "./OSSettingStatusCell";
import { generateWinDiskEncryptionProfile } from "../../helpers";

export interface ITableRowOsSettings extends Omit<IHostMdmProfile, "status"> {
status: OsSettingsTableStatusValue;
}

export type OsSettingsTableStatusValue = MdmProfileStatus | "action_required";

export const isMdmProfileStatus = (
status: string
): status is MdmProfileStatus => {
return status !== "action_required";
};

interface IHeaderProps {
column: {
title: string;
isSortedDesc: boolean;
};
export interface IHostMdmProfileWithAddedStatus
extends Omit<IHostMdmProfile, "status"> {
status: OsSettingsTableStatusValue;
}

interface ICellProps {
cell: {
value: string;
};
row: {
original: ITableRowOsSettings;
};
}
type ITableColumnConfig = Column<IHostMdmProfileWithAddedStatus>;
type ITableStringCellProps = IStringCellProps<IHostMdmProfileWithAddedStatus>;

interface IDataColumn {
Header: ((props: IHeaderProps) => JSX.Element) | string;
Cell: (props: ICellProps) => JSX.Element;
id?: string;
title?: string;
accessor?: string;
disableHidden?: boolean;
disableSortBy?: boolean;
sortType?: string;
}
export type INonDDMProfileStatus = MdmProfileStatus | "action_required";

export type OsSettingsTableStatusValue =
| MdmDDMProfileStatus
| INonDDMProfileStatus;

/**
* generates the formatted tooltip for the error column.
Expand Down Expand Up @@ -107,22 +93,20 @@ const generateErrorTooltip = (
return generateFormattedTooltip(detail);
};

const tableHeaders: IDataColumn[] = [
const tableHeaders: ITableColumnConfig[] = [
{
title: "Name",
Header: "Name",
disableSortBy: true,
accessor: "name",
Cell: (cellProps: ICellProps): JSX.Element => (
<TextCell value={cellProps.cell.value} />
),
Cell: (cellProps: ITableStringCellProps) => {
return <TextCell value={cellProps.cell.value} />;
},
},
{
title: "Status",
Header: "Status",
disableSortBy: true,
accessor: "statusText",
Cell: (cellProps: ICellProps) => {
accessor: "status",
Cell: (cellProps: ITableStringCellProps) => {
return (
<OSSettingStatusCell
status={cellProps.row.original.status}
Expand All @@ -133,11 +117,10 @@ const tableHeaders: IDataColumn[] = [
},
},
{
title: "Error",
Header: "Error",
disableSortBy: true,
accessor: "detail",
Cell: (cellProps: ICellProps): JSX.Element => {
Cell: (cellProps: ITableStringCellProps): JSX.Element => {
const profile = cellProps.row.original;

const value =
Expand Down Expand Up @@ -165,7 +148,7 @@ const tableHeaders: IDataColumn[] = [
];

const makeWindowsRows = ({ profiles, os_settings }: IHostMdmData) => {
const rows: ITableRowOsSettings[] = [];
const rows: IHostMdmProfileWithAddedStatus[] = [];

if (profiles) {
rows.push(...profiles);
Expand All @@ -190,15 +173,12 @@ const makeWindowsRows = ({ profiles, os_settings }: IHostMdmData) => {
return rows;
};

const makeDarwinRows = ({
profiles,
macos_settings,
}: IHostMdmData): ITableRowOsSettings[] | null => {
const makeDarwinRows = ({ profiles, macos_settings }: IHostMdmData) => {
if (!profiles) {
return null;
}

let rows: ITableRowOsSettings[] = profiles;
let rows: IHostMdmProfileWithAddedStatus[] = profiles;
if (macos_settings?.disk_encryption === "action_required") {
rows = profiles.map((p) => {
// TODO: this is a brittle check for the filevault profile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const STATUS_DISPLAY_OPTIONS: StatusDisplayOptions = {
Verified: {
iconName: "success",
tooltipText:
"The host applied all OS settings. Fleet verified with osquery.",
"The host applied all OS settings. Fleet verified with osquery. " +
"Declaration profiles are verified with DDM.",
},
Verifying: {
iconName: "success-outline",
Expand Down
Loading

0 comments on commit d3c8438

Please sign in to comment.