mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
595 lines
16 KiB
TypeScript
595 lines
16 KiB
TypeScript
import React from "react";
|
|
import { find, lowerCase, noop } from "lodash";
|
|
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
|
|
|
|
import { ActivityType, IActivity, IActivityDetails } from "interfaces/activity";
|
|
import { addGravatarUrlToResource } from "utilities/helpers";
|
|
import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
|
|
import Avatar from "components/Avatar";
|
|
import Button from "components/buttons/Button";
|
|
import Icon from "components/Icon";
|
|
import ReactTooltip from "react-tooltip";
|
|
import PremiumFeatureIconWithTooltip from "components/PremiumFeatureIconWithTooltip";
|
|
|
|
const baseClass = "activity-item";
|
|
|
|
const PREMIUM_ACTIVITIES = new Set([
|
|
"created_team",
|
|
"deleted_team",
|
|
"applied_spec_team",
|
|
"changed_user_team_role",
|
|
"deleted_user_team_role",
|
|
"read_host_disk_encryption_key",
|
|
"enabled_macos_disk_encryption",
|
|
"disabled_macos_disk_encryption",
|
|
]);
|
|
|
|
const getProfileMessageSuffix = (
|
|
isPremiumTier: boolean,
|
|
teamName?: string | null
|
|
) => {
|
|
let messageSuffix = <>all macOS hosts</>;
|
|
if (isPremiumTier) {
|
|
messageSuffix = teamName ? (
|
|
<>
|
|
macOS hosts assigned to the <b>{teamName}</b> team
|
|
</>
|
|
) : (
|
|
<>macOS hosts with no team</>
|
|
);
|
|
}
|
|
return messageSuffix;
|
|
};
|
|
|
|
const getDiskEncryptionMessageSuffix = (teamName?: string | null) => {
|
|
return teamName ? (
|
|
<>
|
|
{" "}
|
|
assigned to the <b>{teamName}</b> team
|
|
</>
|
|
) : (
|
|
<>with no team</>
|
|
);
|
|
};
|
|
|
|
const getMacOSSetupAssistantMessage = (
|
|
action: "added" | "deleted",
|
|
name?: string,
|
|
teamName?: string | null
|
|
) => {
|
|
const suffix = teamName ? (
|
|
<>
|
|
{" "}
|
|
that automatically enroll to the <b>{teamName}</b> team
|
|
</>
|
|
) : (
|
|
<>that automatically enroll to no team</>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{" "}
|
|
changed the macOS Setup Assistant ({action} <b>{name}</b>) for hosts{" "}
|
|
{suffix}.
|
|
</>
|
|
);
|
|
};
|
|
|
|
const TAGGED_TEMPLATES = {
|
|
liveQueryActivityTemplate: (
|
|
activity: IActivity,
|
|
onDetailsClick?: (details: IActivityDetails) => void
|
|
) => {
|
|
const count = activity.details?.targets_count;
|
|
const queryName = activity.details?.query_name;
|
|
const querySql = activity.details?.query_sql;
|
|
|
|
const savedQueryName = queryName ? (
|
|
<>
|
|
the <b>{queryName}</b> query as
|
|
</>
|
|
) : (
|
|
<></>
|
|
);
|
|
|
|
const hostCount =
|
|
count !== undefined
|
|
? ` on ${count} ${count === 1 ? "host" : "hosts"}`
|
|
: "";
|
|
|
|
return (
|
|
<>
|
|
<span>
|
|
ran {savedQueryName} a live query {hostCount}.
|
|
</span>
|
|
{querySql && (
|
|
<>
|
|
<Button
|
|
className={`${baseClass}__show-query-link`}
|
|
variant="text-link"
|
|
onClick={() => onDetailsClick?.({ query_sql: querySql })}
|
|
>
|
|
Show query{" "}
|
|
<Icon className={`${baseClass}__show-query-icon`} name="eye" />
|
|
</Button>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
},
|
|
editPackCtlActivityTemplate: () => {
|
|
return "edited a pack using fleetctl.";
|
|
},
|
|
editPolicyCtlActivityTemplate: () => {
|
|
return "edited policies using fleetctl.";
|
|
},
|
|
editQueryCtlActivityTemplate: (activity: IActivity) => {
|
|
const count = activity.details?.specs?.length;
|
|
return typeof count === "undefined" || count === 1
|
|
? "edited a query using fleetctl."
|
|
: "edited queries using fleetctl.";
|
|
},
|
|
editTeamCtlActivityTemplate: (activity: IActivity) => {
|
|
const count = activity.details?.teams?.length;
|
|
return count === 1 && activity.details?.teams ? (
|
|
<>
|
|
edited the <b>{activity.details?.teams[0].name}</b> team using fleetctl.
|
|
</>
|
|
) : (
|
|
"edited multiple teams using fleetctl."
|
|
);
|
|
},
|
|
editAgentOptions: (activity: IActivity) => {
|
|
return activity.details?.global ? (
|
|
"edited agent options."
|
|
) : (
|
|
<>
|
|
edited agent options on <b>{activity.details?.team_name}</b> team.
|
|
</>
|
|
);
|
|
},
|
|
userAddedBySSOTempalte: () => {
|
|
return "was added to Fleet by SSO.";
|
|
},
|
|
userLoggedIn: (activity: IActivity) => {
|
|
return `successfully logged in from public IP ${activity.details?.public_ip}.`;
|
|
},
|
|
userFailedLogin: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
Somebody using <b>{activity.details?.email}</b> failed to log in from
|
|
public IP {activity.details?.public_ip}.
|
|
</>
|
|
);
|
|
},
|
|
userCreated: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
created a user <b> {activity.details?.user_email}</b>.
|
|
</>
|
|
);
|
|
},
|
|
userDeleted: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
deleted a user <b>{activity.details?.user_email}</b>.
|
|
</>
|
|
);
|
|
},
|
|
userChangedGlobalRole: (activity: IActivity, isPremiumTier: boolean) => {
|
|
return (
|
|
<>
|
|
changed <b>{activity.details?.user_email}</b> to{" "}
|
|
<b>{activity.details?.role}</b>
|
|
{isPremiumTier && " for all teams"}.
|
|
</>
|
|
);
|
|
},
|
|
userDeletedGlobalRole: (activity: IActivity, isPremiumTier: boolean) => {
|
|
return (
|
|
<>
|
|
removed <b>{activity.details?.user_email}</b> as{" "}
|
|
<b>{activity.details?.role}</b>
|
|
{isPremiumTier && " for all teams"}.
|
|
</>
|
|
);
|
|
},
|
|
userChangedTeamRole: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
changed <b>{activity.details?.user_email}</b> to{" "}
|
|
<b>{activity.details?.role}</b> for the{" "}
|
|
<b>{activity.details?.team_name}</b> team.
|
|
</>
|
|
);
|
|
},
|
|
userDeletedTeamRole: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
removed <b>{activity.details?.user_email}</b> from the{" "}
|
|
<b>{activity.details?.team_name}</b> team.
|
|
</>
|
|
);
|
|
},
|
|
mdmEnrolled: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
An end user turned on MDM features for a host with serial number{" "}
|
|
<b>
|
|
{activity.details?.host_serial} (
|
|
{activity.details?.installed_from_dep ? "automatic" : "manual"})
|
|
</b>
|
|
.
|
|
</>
|
|
);
|
|
},
|
|
mdmUnenrolled: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
{activity.actor_full_name
|
|
? " told Fleet to turn off mobile device management (MDM) for"
|
|
: "Mobile device management (MDM) was turned off for"}{" "}
|
|
<b>{activity.details?.host_display_name}</b>.
|
|
</>
|
|
);
|
|
},
|
|
editedMacosMinVersion: (activity: IActivity) => {
|
|
const editedActivity =
|
|
activity.details?.minimum_version === "" ? "removed" : "updated";
|
|
|
|
const versionSection = activity.details?.minimum_version ? (
|
|
<>
|
|
to <b>{activity.details.minimum_version}</b>
|
|
</>
|
|
) : null;
|
|
|
|
const deadlineSection = activity.details?.deadline ? (
|
|
<>(deadline: {activity.details.deadline})</>
|
|
) : null;
|
|
|
|
const teamSection = activity.details?.team_id ? (
|
|
<>
|
|
the <b>{activity.details.team_name}</b> team
|
|
</>
|
|
) : (
|
|
<>no team</>
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{editedActivity} the minimum macOS version {versionSection}{" "}
|
|
{deadlineSection} on hosts assigned to {teamSection}.
|
|
</>
|
|
);
|
|
},
|
|
|
|
readHostDiskEncryptionKey: (activity: IActivity) => {
|
|
return (
|
|
<>
|
|
{" "}
|
|
viewed the disk encryption key for{" "}
|
|
<b>{activity.details?.host_display_name}</b>.
|
|
</>
|
|
);
|
|
},
|
|
|
|
createMacOSProfile: (activity: IActivity, isPremiumTier: boolean) => {
|
|
const profileName = activity.details?.profile_name;
|
|
return (
|
|
<>
|
|
{" "}
|
|
added{" "}
|
|
{profileName ? (
|
|
<>configuration profile {profileName}</>
|
|
) : (
|
|
<>a configuration profile</>
|
|
)}{" "}
|
|
to {getProfileMessageSuffix(isPremiumTier, activity.details?.team_name)}
|
|
.
|
|
</>
|
|
);
|
|
},
|
|
|
|
deleteMacOSProfile: (activity: IActivity, isPremiumTier: boolean) => {
|
|
const profileName = activity.details?.profile_name;
|
|
return (
|
|
<>
|
|
{" "}
|
|
deleted{" "}
|
|
{profileName ? (
|
|
<>configuration profile {profileName}</>
|
|
) : (
|
|
<>a configuration profile</>
|
|
)}{" "}
|
|
from{" "}
|
|
{getProfileMessageSuffix(isPremiumTier, activity.details?.team_name)}.
|
|
</>
|
|
);
|
|
},
|
|
|
|
editMacOSProfile: (activity: IActivity, isPremiumTier: boolean) => {
|
|
return (
|
|
<>
|
|
{" "}
|
|
edited configuration profiles for{" "}
|
|
{getProfileMessageSuffix(
|
|
isPremiumTier,
|
|
activity.details?.team_name
|
|
)}{" "}
|
|
via fleetctl.
|
|
</>
|
|
);
|
|
},
|
|
enableMacDiskEncryption: (activity: IActivity) => {
|
|
const suffix = getDiskEncryptionMessageSuffix(activity.details?.team_name);
|
|
return <> enforced disk encryption for macOS hosts {suffix}.</>;
|
|
},
|
|
disableMacDiskEncryption: (activity: IActivity) => {
|
|
const suffix = getDiskEncryptionMessageSuffix(activity.details?.team_name);
|
|
return <>removed disk encryption enforcement for macOS hosts {suffix}.</>;
|
|
},
|
|
changedMacOSSetupAssistant: (activity: IActivity) => {
|
|
return getMacOSSetupAssistantMessage(
|
|
"added",
|
|
activity.details?.name,
|
|
activity.details?.team_name
|
|
);
|
|
},
|
|
deletedMacOSSetupAssistant: (activity: IActivity) => {
|
|
return getMacOSSetupAssistantMessage(
|
|
"deleted",
|
|
activity.details?.name,
|
|
activity.details?.team_name
|
|
);
|
|
},
|
|
defaultActivityTemplate: (activity: IActivity) => {
|
|
const entityName = find(activity.details, (_, key) =>
|
|
key.includes("_name")
|
|
);
|
|
|
|
const activityType = lowerCase(activity.type).replace(" saved", "");
|
|
|
|
return !entityName ? (
|
|
`${activityType}.`
|
|
) : (
|
|
<>
|
|
{activityType} <b>{entityName}</b>.
|
|
</>
|
|
);
|
|
},
|
|
addedMDMBootstrapPackage: (activity: IActivity) => {
|
|
const packageName = activity.details?.bootstrap_package_name;
|
|
return (
|
|
<>
|
|
{" "}
|
|
added a bootstrap package{" "}
|
|
{packageName ? (
|
|
<>
|
|
(<b>{packageName}</b>){" "}
|
|
</>
|
|
) : (
|
|
""
|
|
)}
|
|
for macOS hosts that automatically enroll to{" "}
|
|
{activity.details?.team_name ? (
|
|
<>
|
|
the <b>{activity.details.team_name}</b> team
|
|
</>
|
|
) : (
|
|
"no team"
|
|
)}
|
|
.
|
|
</>
|
|
);
|
|
},
|
|
deletedMDMBootstrapPackage: (activity: IActivity) => {
|
|
const packageName = activity.details?.bootstrap_package_name;
|
|
return (
|
|
<>
|
|
{" "}
|
|
deleted a bootstrap package{" "}
|
|
{packageName ? (
|
|
<>
|
|
(<b>{packageName}</b>){" "}
|
|
</>
|
|
) : (
|
|
""
|
|
)}
|
|
for macOS hosts that automatically enroll to{" "}
|
|
{activity.details?.team_name ? (
|
|
<>
|
|
the <b>{activity.details.team_name}</b> team
|
|
</>
|
|
) : (
|
|
"no team"
|
|
)}
|
|
.
|
|
</>
|
|
);
|
|
},
|
|
};
|
|
|
|
const getDetail = (
|
|
activity: IActivity,
|
|
isPremiumTier: boolean,
|
|
onDetailsClick?: (details: IActivityDetails) => void
|
|
) => {
|
|
switch (activity.type) {
|
|
case ActivityType.LiveQuery: {
|
|
return TAGGED_TEMPLATES.liveQueryActivityTemplate(
|
|
activity,
|
|
onDetailsClick
|
|
);
|
|
}
|
|
case ActivityType.AppliedSpecPack: {
|
|
return TAGGED_TEMPLATES.editPackCtlActivityTemplate();
|
|
}
|
|
case ActivityType.AppliedSpecPolicy: {
|
|
return TAGGED_TEMPLATES.editPolicyCtlActivityTemplate();
|
|
}
|
|
case ActivityType.AppliedSpecSavedQuery: {
|
|
return TAGGED_TEMPLATES.editQueryCtlActivityTemplate(activity);
|
|
}
|
|
case ActivityType.AppliedSpecTeam: {
|
|
return TAGGED_TEMPLATES.editTeamCtlActivityTemplate(activity);
|
|
}
|
|
case ActivityType.EditedAgentOptions: {
|
|
return TAGGED_TEMPLATES.editAgentOptions(activity);
|
|
}
|
|
case ActivityType.UserAddedBySSO: {
|
|
return TAGGED_TEMPLATES.userAddedBySSOTempalte();
|
|
}
|
|
case ActivityType.UserLoggedIn: {
|
|
return TAGGED_TEMPLATES.userLoggedIn(activity);
|
|
}
|
|
case ActivityType.UserFailedLogin: {
|
|
return TAGGED_TEMPLATES.userFailedLogin(activity);
|
|
}
|
|
case ActivityType.UserCreated: {
|
|
return TAGGED_TEMPLATES.userCreated(activity);
|
|
}
|
|
case ActivityType.UserDeleted: {
|
|
return TAGGED_TEMPLATES.userDeleted(activity);
|
|
}
|
|
case ActivityType.UserChangedGlobalRole: {
|
|
return TAGGED_TEMPLATES.userChangedGlobalRole(activity, isPremiumTier);
|
|
}
|
|
case ActivityType.UserDeletedGlobalRole: {
|
|
return TAGGED_TEMPLATES.userDeletedGlobalRole(activity, isPremiumTier);
|
|
}
|
|
case ActivityType.UserChangedTeamRole: {
|
|
return TAGGED_TEMPLATES.userChangedTeamRole(activity);
|
|
}
|
|
case ActivityType.UserDeletedTeamRole: {
|
|
return TAGGED_TEMPLATES.userDeletedTeamRole(activity);
|
|
}
|
|
case ActivityType.MdmEnrolled: {
|
|
return TAGGED_TEMPLATES.mdmEnrolled(activity);
|
|
}
|
|
case ActivityType.MdmUnenrolled: {
|
|
return TAGGED_TEMPLATES.mdmUnenrolled(activity);
|
|
}
|
|
case ActivityType.EditedMacosMinVersion: {
|
|
return TAGGED_TEMPLATES.editedMacosMinVersion(activity);
|
|
}
|
|
case ActivityType.ReadHostDiskEncryptionKey: {
|
|
return TAGGED_TEMPLATES.readHostDiskEncryptionKey(activity);
|
|
}
|
|
case ActivityType.CreatedMacOSProfile: {
|
|
return TAGGED_TEMPLATES.createMacOSProfile(activity, isPremiumTier);
|
|
}
|
|
case ActivityType.DeletedMacOSProfile: {
|
|
return TAGGED_TEMPLATES.deleteMacOSProfile(activity, isPremiumTier);
|
|
}
|
|
case ActivityType.EditedMacOSProfile: {
|
|
return TAGGED_TEMPLATES.editMacOSProfile(activity, isPremiumTier);
|
|
}
|
|
case ActivityType.EnabledMacDiskEncryption: {
|
|
return TAGGED_TEMPLATES.enableMacDiskEncryption(activity);
|
|
}
|
|
case ActivityType.DisabledMacDiskEncryption: {
|
|
return TAGGED_TEMPLATES.disableMacDiskEncryption(activity);
|
|
}
|
|
case ActivityType.AddedBootstrapPackage: {
|
|
return TAGGED_TEMPLATES.addedMDMBootstrapPackage(activity);
|
|
}
|
|
case ActivityType.DeletedBootstrapPackage: {
|
|
return TAGGED_TEMPLATES.deletedMDMBootstrapPackage(activity);
|
|
}
|
|
case ActivityType.ChangedMacOSSetupAssistant: {
|
|
return TAGGED_TEMPLATES.changedMacOSSetupAssistant(activity);
|
|
}
|
|
case ActivityType.DeletedMacOSSetupAssistant: {
|
|
return TAGGED_TEMPLATES.deletedMacOSSetupAssistant(activity);
|
|
}
|
|
default: {
|
|
return TAGGED_TEMPLATES.defaultActivityTemplate(activity);
|
|
}
|
|
}
|
|
};
|
|
|
|
interface IActivityItemProps {
|
|
activity: IActivity;
|
|
isPremiumTier: boolean;
|
|
isSandboxMode?: boolean;
|
|
|
|
/** A handler for handling clicking on the details of an activity. Not all
|
|
* activites have more details so this is optional. An example of additonal
|
|
* details is showing the query for a live query action.
|
|
*/
|
|
onDetailsClick?: (details: IActivityDetails) => void;
|
|
}
|
|
|
|
const ActivityItem = ({
|
|
activity,
|
|
isPremiumTier,
|
|
isSandboxMode = false,
|
|
onDetailsClick = noop,
|
|
}: IActivityItemProps) => {
|
|
const { actor_email } = activity;
|
|
const { gravatar_url } = actor_email
|
|
? addGravatarUrlToResource({ email: actor_email })
|
|
: { gravatar_url: DEFAULT_GRAVATAR_LINK };
|
|
|
|
const activityCreatedAt = new Date(activity.created_at);
|
|
const indicatePremiumFeature =
|
|
isSandboxMode && PREMIUM_ACTIVITIES.has(activity.type);
|
|
|
|
return (
|
|
<div className={baseClass}>
|
|
<Avatar
|
|
className={`${baseClass}__avatar-image`}
|
|
user={{ gravatar_url }}
|
|
size="small"
|
|
hasWhiteBackground
|
|
/>
|
|
<div className={`${baseClass}__details`}>
|
|
<p>
|
|
{indicatePremiumFeature && <PremiumFeatureIconWithTooltip />}
|
|
<span className={`${baseClass}__details-topline`}>
|
|
{activity.type === ActivityType.UserLoggedIn ? (
|
|
<b>{activity.actor_email} </b>
|
|
) : (
|
|
<b>{activity.actor_full_name} </b>
|
|
)}
|
|
{getDetail(activity, isPremiumTier, onDetailsClick)}
|
|
</span>
|
|
<br />
|
|
<span
|
|
className={`${baseClass}__details-bottomline`}
|
|
data-tip
|
|
data-for={`activity-${activity.id}`}
|
|
>
|
|
{formatDistanceToNowStrict(activityCreatedAt, {
|
|
addSuffix: true,
|
|
})}
|
|
</span>
|
|
<ReactTooltip
|
|
className="date-tooltip"
|
|
place="top"
|
|
type="dark"
|
|
effect="solid"
|
|
id={`activity-${activity.id}`}
|
|
backgroundColor="#3e4771"
|
|
>
|
|
{intlFormat(
|
|
activityCreatedAt,
|
|
{
|
|
year: "numeric",
|
|
month: "numeric",
|
|
day: "numeric",
|
|
hour: "numeric",
|
|
minute: "numeric",
|
|
second: "numeric",
|
|
},
|
|
{ locale: window.navigator.languages[0] }
|
|
)}
|
|
</ReactTooltip>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ActivityItem;
|