Host details page: Surface disk encryption information (#8437)

This commit is contained in:
RachelElysia 2022-11-07 16:51:03 -05:00 committed by GitHub
parent e1120f06e0
commit 6921e43bb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 56 deletions

View file

@ -0,0 +1,2 @@
- Host details page includes information about the host's disk encryption
- Information surfaced to device user includes all summary/about information surfaced in host details page

View file

@ -193,4 +193,5 @@ export interface IHost {
query_results?: unknown[];
geolocation?: IGeoLocation;
batteries?: IBattery[];
disk_encryption_enabled?: boolean;
}

View file

@ -8,7 +8,12 @@ import { pick } from "lodash";
import { NotificationContext } from "context/notification";
import deviceUserAPI from "services/entities/device_user";
import { IHost, IDeviceMappingResponse } from "interfaces/host";
import hostAPI from "services/entities/hosts";
import {
IHost,
IDeviceMappingResponse,
IMacadminsResponse,
} from "interfaces/host";
import { ISoftware } from "interfaces/software";
import { IHostPolicy } from "interfaces/policy";
import DeviceUserError from "components/DeviceUserError";
@ -17,7 +22,11 @@ import OrgLogoIcon from "components/icons/OrgLogoIcon";
import Spinner from "components/Spinner";
import Button from "components/buttons/Button";
import TabsWrapper from "components/TabsWrapper";
import { normalizeEmptyValues, wrapFleetHelper } from "utilities/helpers";
import {
normalizeEmptyValues,
wrapFleetHelper,
humanHostDiskEncryptionEnabled,
} from "utilities/helpers";
import HostSummaryCard from "../cards/HostSummary";
import AboutCard from "../cards/About";
@ -47,6 +56,13 @@ interface IHostResponse {
host: IHost;
org_logo_url: string;
license: ILicense;
disk_encryption_enabled?: boolean;
platform?: string;
}
interface IHostDiskEncryptionProps {
enabled?: boolean;
tooltip?: string;
}
const DeviceUserPage = ({
@ -60,6 +76,10 @@ const DeviceUserPage = ({
const [refetchStartTime, setRefetchStartTime] = useState<number | null>(null);
const [showRefetchSpinner, setShowRefetchSpinner] = useState(false);
const [hostSoftware, setHostSoftware] = useState<ISoftware[]>([]);
const [
hostDiskEncryption,
setHostDiskEncryption,
] = useState<IHostDiskEncryptionProps>({});
const [host, setHost] = useState<IHost | null>();
const [orgLogoURL, setOrgLogoURL] = useState("");
const [selectedPolicy, setSelectedPolicy] = useState<IHostPolicy | null>(
@ -81,10 +101,22 @@ const DeviceUserPage = ({
}
);
const { data: macadmins, refetch: refetchMacadmins } = useQuery(
["macadmins", deviceAuthToken],
() => deviceUserAPI.loadHostDetailsExtension(deviceAuthToken, "macadmins"),
{
enabled: !!deviceAuthToken,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
retry: false,
select: (data: IMacadminsResponse) => data.macadmins,
}
);
const refetchExtensions = () => {
deviceMapping !== null && refetchDeviceMapping();
};
const {
isLoading: isLoadingHost,
error: loadingDeviceUserError,
@ -104,6 +136,13 @@ const DeviceUserPage = ({
setIsPremiumTier(returnedHost.license.tier === "premium");
setHostSoftware(returnedHost.host.software ?? []);
setHost(returnedHost.host);
setHostDiskEncryption({
enabled: returnedHost.host.disk_encryption_enabled,
tooltip: humanHostDiskEncryptionEnabled(
returnedHost.host.platform,
returnedHost.host.disk_encryption_enabled
),
});
setOrgLogoURL(returnedHost.org_logo_url);
if (returnedHost?.host.refetch_requested) {
// If the API reports that a Fleet refetch request is pending, we want to check back for fresh
@ -165,6 +204,7 @@ const DeviceUserPage = ({
"detail_updated_at",
"percent_disk_space_available",
"gigs_disk_space_available",
"team_name",
])
);
@ -177,6 +217,7 @@ const DeviceUserPage = ({
"hardware_serial",
"primary_ip",
"public_ip",
"geolocation",
"batteries",
"detail_updated_at",
])
@ -241,6 +282,7 @@ const DeviceUserPage = ({
<HostSummaryCard
statusClassName={statusClassName}
titleData={titleData}
diskEncryption={hostDiskEncryption}
showRefetchSpinner={showRefetchSpinner}
onRefetchHost={onRefetchHost}
renderActionButtons={renderActionButtons}
@ -267,6 +309,7 @@ const DeviceUserPage = ({
<AboutCard
aboutData={aboutData}
deviceMapping={deviceMapping}
macadmins={macadmins}
wrapFleetHelper={wrapFleetHelper}
deviceUser
/>

View file

@ -73,6 +73,12 @@
}
}
}
// Position tooltip over parent div
.component__tooltip-wrapper {
position: absolute;
margin-top: 20px;
}
}
&__header {

View file

@ -36,7 +36,11 @@ import TabsWrapper from "components/TabsWrapper";
import MainContent from "components/MainContent";
import BackLink from "components/BackLink";
import { normalizeEmptyValues, wrapFleetHelper } from "utilities/helpers";
import {
normalizeEmptyValues,
humanHostDiskEncryptionEnabled,
wrapFleetHelper,
} from "utilities/helpers";
import HostSummaryCard from "../cards/HostSummary";
import AboutCard from "../cards/About";
@ -52,7 +56,7 @@ import SelectQueryModal from "./modals/SelectQueryModal";
import TransferHostModal from "./modals/TransferHostModal";
import PolicyDetailsModal from "../cards/Policies/HostPoliciesTable/PolicyDetailsModal";
import DeleteHostModal from "./modals/DeleteHostModal";
import RenderOSPolicyModal from "./modals/OSPolicyModal";
import OSPolicyModal from "./modals/OSPolicyModal";
import parseOsVersion from "./modals/OSPolicyModal/helpers";
import DeleteIcon from "../../../../../assets/images/icon-action-delete-14x14@2x.png";
@ -81,6 +85,11 @@ interface ISearchQueryData {
pageIndex: number;
}
interface IHostDiskEncryptionProps {
enabled?: boolean;
tooltip?: string;
}
const TAGGED_TEMPLATES = {
queryByHostRoute: (hostId: number | undefined | null) => {
return `${hostId ? `?host_ids=${hostId}` : ""}`;
@ -139,6 +148,10 @@ const HostDetailsPage = ({
const [packsState, setPacksState] = useState<IPackStats[]>();
const [scheduleState, setScheduleState] = useState<IQueryStats[]>();
const [hostSoftware, setHostSoftware] = useState<ISoftware[]>([]);
const [
hostDiskEncryption,
setHostDiskEncryption,
] = useState<IHostDiskEncryptionProps>({});
const [usersState, setUsersState] = useState<{ username: string }[]>([]);
const [usersSearchString, setUsersSearchString] = useState("");
@ -261,6 +274,13 @@ const HostDetailsPage = ({
}
setHostSoftware(returnedHost.software || []);
setUsersState(returnedHost.users || []);
setHostDiskEncryption({
enabled: returnedHost.disk_encryption_enabled,
tooltip: humanHostDiskEncryptionEnabled(
returnedHost.platform,
returnedHost.disk_encryption_enabled
),
});
if (returnedHost.pack_stats) {
const packStatsByType = returnedHost.pack_stats.reduce(
(
@ -543,6 +563,7 @@ const HostDetailsPage = ({
<HostSummaryCard
statusClassName={statusClassName}
titleData={titleData}
diskEncryption={hostDiskEncryption}
isPremiumTier={isPremiumTier}
isOnlyObserver={isOnlyObserver}
toggleOSPolicyModal={toggleOSPolicyModal}
@ -656,10 +677,11 @@ const HostDetailsPage = ({
/>
)}
{showOSPolicyModal && (
<RenderOSPolicyModal
<OSPolicyModal
onCancel={() => setShowOSPolicyModal(false)}
onCreateNewPolicy={onCreateNewPolicy}
titleData={titleData}
osVersion={host?.os_version}
detailsUpdatedAt={host?.detail_updated_at}
osPolicy={osPolicyQuery}
osPolicyLabel={osPolicyLabel}
/>

View file

@ -61,6 +61,12 @@
}
}
}
// Position tooltip over parent div
.component__tooltip-wrapper {
position: absolute;
margin-top: 20px;
}
}
&__header {

View file

@ -16,7 +16,8 @@ import CopyIcon from "../../../../../../../assets/images/icon-copy-clipboard-fle
interface IRenderOSPolicyModal {
onCreateNewPolicy: (team: ITeam) => void;
onCancel: () => void;
titleData?: any;
osVersion?: string;
detailsUpdatedAt?: string;
osPolicy: string;
osPolicyLabel: string;
}
@ -26,7 +27,8 @@ const baseClass = "os-policy-modal";
const RenderOSPolicyModal = ({
onCancel,
onCreateNewPolicy,
titleData,
osVersion,
detailsUpdatedAt,
osPolicy,
osPolicyLabel,
}: IRenderOSPolicyModal): JSX.Element => {
@ -73,11 +75,9 @@ const RenderOSPolicyModal = ({
<Modal title="Operating system" onExit={onCancel} className={baseClass}>
<>
<p>
<span className={`${baseClass}__os-modal-title`}>
{titleData.os_version}{" "}
</span>
<span className={`${baseClass}__os-modal-title`}>{osVersion} </span>
<span className={`${baseClass}__os-modal-updated`}>
Reported {humanHostDetailUpdated(titleData.detail_updated_at)}
Reported {humanHostDetailUpdated(detailsUpdatedAt)}
</span>
</p>
<span className={`${baseClass}__os-modal-example-title`}>

View file

@ -152,38 +152,6 @@ const About = ({
);
};
if (deviceUser) {
return (
<div className="section about">
<p className="section__header">About</p>
<div className="info-grid">
<div className="info-grid__block">
<span className="info-grid__header">Last restarted</span>
<span className="info-grid__data">
{humanHostLastRestart(
aboutData.detail_updated_at,
aboutData.uptime
)}
</span>
</div>
<div className="info-grid__block">
<span className="info-grid__header">Hardware model</span>
<span className="info-grid__data">{aboutData.hardware_model}</span>
</div>
<div className="info-grid__block">
<span className="info-grid__header">Added to Fleet</span>
<span className="info-grid__data">
{wrapFleetHelper(humanHostEnrolled, aboutData.last_enrolled_at)}
</span>
</div>
{renderSerialAndIPs()}
{renderDeviceUser()}
{renderBattery()}
</div>
</div>
);
}
return (
<div className="section about">
<p className="section__header">About</p>

View file

@ -1,6 +1,7 @@
import React from "react";
import ReactTooltip from "react-tooltip";
import TooltipWrapper from "components/TooltipWrapper";
import Button from "components/buttons/Button";
import DiskSpaceGraph from "components/DiskSpaceGraph";
@ -13,9 +14,14 @@ import IssueIcon from "../../../../../../assets/images/icon-issue-fleet-black-50
const baseClass = "host-summary";
interface IHostDiskEncryptionProps {
enabled?: boolean;
tooltip?: string;
}
interface IHostSummaryProps {
statusClassName: string;
titleData: any; // TODO: create interfaces for this and use consistently across host pages and related helpers
diskEncryption?: IHostDiskEncryptionProps;
isPremiumTier?: boolean;
isOnlyObserver?: boolean;
toggleOSPolicyModal?: () => void;
@ -30,6 +36,7 @@ interface IHostSummaryProps {
const HostSummary = ({
statusClassName,
titleData,
diskEncryption,
isPremiumTier,
isOnlyObserver,
toggleOSPolicyModal,
@ -131,13 +138,9 @@ const HostSummary = ({
</span>
</div>
{titleData.issues?.total_issues_count > 0 &&
deviceUser &&
isPremiumTier &&
renderIssues()}
{titleData.issues?.total_issues_count > 0 &&
!deviceUser &&
renderIssues()}
{!deviceUser && isPremiumTier && renderHostTeam()}
{isPremiumTier && renderHostTeam()}
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Disk space</span>
<DiskSpaceGraph
@ -147,6 +150,17 @@ const HostSummary = ({
id={"disk-space-tooltip"}
/>
</div>
{typeof diskEncryption?.enabled === "boolean" &&
diskEncryption?.tooltip ? (
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Disk encryption</span>
<TooltipWrapper tipContent={diskEncryption.tooltip}>
{diskEncryption.enabled ? "On" : "Off"}
</TooltipWrapper>
</div>
) : (
<></>
)}
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Memory</span>
<span className="info-flex__data">
@ -173,12 +187,10 @@ const HostSummary = ({
)}
</span>
</div>
{!deviceUser && (
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Osquery</span>
<span className="info-flex__data">{titleData.osquery_version}</span>
</div>
)}
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Osquery</span>
<span className="info-flex__data">{titleData.osquery_version}</span>
</div>
</div>
);
};

View file

@ -617,6 +617,31 @@ export const humanHostDetailUpdated = (detailUpdated?: string): string => {
}
};
const DISK_ENCRYPTION_MESSAGES = {
darwin: {
enabled:
"The disk is encrypted. The user must enter their<br/> password when they start their computer.",
disabled:
"The disk might be encrypted, but FileVault is off. The<br/> disk can be accessed without entering a password.",
},
windows: {
enabled:
"The disk is encrypted. If recently turned on,<br/> encryption could take awhile.",
disabled: "The disk is unencrypted.",
},
};
export const humanHostDiskEncryptionEnabled = (
platform?: string,
isDiskEncrypted = false
): string => {
if (platform !== "windows" && platform !== "darwin") {
return "Disk encryption is enabled.";
}
const encryptionStatus = isDiskEncrypted ? "enabled" : "disabled";
return DISK_ENCRYPTION_MESSAGES[platform][encryptionStatus];
};
export const hostTeamName = (teamName: string | null): string => {
if (!teamName) {
return "No team";
@ -805,6 +830,7 @@ export default {
humanHostEnrolled,
humanHostMemory,
humanHostDetailUpdated,
humanHostDiskEncryptionEnabled,
hostTeamName,
humanQueryLastRun,
inMilliseconds,