mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Cancel UA: Make wiped activity a host-specific activity so it can be deleted when canceled (#28034)
For #27409 (unreleased bug) # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] Added/updated automated tests - [x] Manual QA for all new/changed functionality See https://drive.google.com/file/d/1xg8DM97UJITA0vGUyoOd2esZRfehEgW7/view?usp=drive_link
This commit is contained in:
parent
d5ba6a983b
commit
e33b2b0a41
7 changed files with 79 additions and 6 deletions
|
|
@ -119,6 +119,7 @@ export enum ActivityType {
|
|||
export type IHostPastActivityType =
|
||||
| ActivityType.RanScript
|
||||
| ActivityType.LockedHost
|
||||
| ActivityType.WipedHost
|
||||
| ActivityType.UnlockedHost
|
||||
| ActivityType.InstalledSoftware
|
||||
| ActivityType.UninstalledSoftware
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { ShowActivityDetailsHandler } from "components/ActivityItem/ActivityItem
|
|||
|
||||
import RanScriptActivityItem from "./ActivityItems/RanScriptActivityItem";
|
||||
import LockedHostActivityItem from "./ActivityItems/LockedHostActivityItem";
|
||||
import WipedHostActivityItem from "./ActivityItems/WipedHostActivityItem";
|
||||
import UnlockedHostActivityItem from "./ActivityItems/UnlockedHostActivityItem";
|
||||
import InstalledSoftwareActivityItem from "./ActivityItems/InstalledSoftwareActivityItem";
|
||||
import CanceledRunScriptActivityItem from "./ActivityItems/CanceledRunScriptActivityItem";
|
||||
|
|
@ -46,6 +47,7 @@ export const pastActivityComponentMap: Record<
|
|||
> = {
|
||||
[ActivityType.RanScript]: RanScriptActivityItem,
|
||||
[ActivityType.LockedHost]: LockedHostActivityItem,
|
||||
[ActivityType.WipedHost]: WipedHostActivityItem,
|
||||
[ActivityType.UnlockedHost]: UnlockedHostActivityItem,
|
||||
[ActivityType.InstalledSoftware]: InstalledSoftwareActivityItem,
|
||||
[ActivityType.UninstalledSoftware]: InstalledSoftwareActivityItem,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createMockHostPastActivity } from "__mocks__/activityMock";
|
||||
|
||||
import WipeHostActivityItem from "./WipedHostActivityItem";
|
||||
|
||||
describe("WipeHostActivityItem", () => {
|
||||
it("renders the activity content", () => {
|
||||
render(
|
||||
<WipeHostActivityItem
|
||||
activity={createMockHostPastActivity({ actor_full_name: "Test User" })}
|
||||
tab="past"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Test User")).toBeVisible();
|
||||
expect(screen.getByText(/wiped this host/i)).toBeVisible();
|
||||
});
|
||||
|
||||
it("does not render the cancel icon", () => {
|
||||
render(
|
||||
<WipeHostActivityItem
|
||||
activity={createMockHostPastActivity({ actor_full_name: "Test User" })}
|
||||
tab="past"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("close-icon")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render the show details icon", () => {
|
||||
render(
|
||||
<WipeHostActivityItem
|
||||
activity={createMockHostPastActivity({ actor_full_name: "Test User" })}
|
||||
tab="past"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("info-outline-icon")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
|
||||
import ActivityItem from "components/ActivityItem";
|
||||
|
||||
import { IHostActivityItemComponentProps } from "../../ActivityConfig";
|
||||
|
||||
const baseClass = "wiped-host-activity-item";
|
||||
|
||||
const WipedHostActivityItem = ({
|
||||
activity,
|
||||
}: IHostActivityItemComponentProps) => {
|
||||
return (
|
||||
<ActivityItem
|
||||
className={baseClass}
|
||||
activity={activity}
|
||||
hideCancel
|
||||
hideShowDetails
|
||||
>
|
||||
<b>{activity.actor_full_name}</b> wiped this host.
|
||||
</ActivityItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default WipedHostActivityItem;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./WipedHostActivityItem";
|
||||
|
|
@ -959,23 +959,23 @@ func clearLockWipeForCanceledActivity(ctx context.Context, tx sqlx.ExtContext, h
|
|||
lockCnt, _ := resLock.RowsAffected()
|
||||
wipeCnt, _ := resWipe.RowsAffected()
|
||||
if lockCnt > 0 || wipeCnt > 0 {
|
||||
// if it did deleted host_mdm_actions, then it was a lock or wipe activity,
|
||||
// we need to deleted the "past" activity that gets created immediately
|
||||
// if it did delete host_mdm_actions, then it was a lock or wipe activity,
|
||||
// we need to delete the "past" activity that gets created immediately
|
||||
// when that command is queued.
|
||||
actType := fleet.ActivityTypeLockedHost{}.ActivityName()
|
||||
if wipeCnt > 0 {
|
||||
actType = fleet.ActivityTypeWipedHost{}.ActivityName()
|
||||
}
|
||||
|
||||
const findActStmt = `SELECT
|
||||
id
|
||||
const findActStmt = `SELECT
|
||||
id
|
||||
FROM
|
||||
activities
|
||||
activities
|
||||
INNER JOIN host_activities ON (host_activities.activity_id = activities.id)
|
||||
WHERE
|
||||
host_activities.host_id = ? AND
|
||||
activities.activity_type = ?
|
||||
ORDER BY
|
||||
ORDER BY
|
||||
activities.created_at DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
|
|
|||
|
|
@ -1597,6 +1597,10 @@ func (a ActivityTypeWipedHost) ActivityName() string {
|
|||
return "wiped_host"
|
||||
}
|
||||
|
||||
func (a ActivityTypeWipedHost) HostIDs() []uint {
|
||||
return []uint{a.HostID}
|
||||
}
|
||||
|
||||
func (a ActivityTypeWipedHost) Documentation() (activity, details, detailsExample string) {
|
||||
return `Generated when a user sends a request to wipe a host.`,
|
||||
`This activity contains the following fields:
|
||||
|
|
|
|||
Loading…
Reference in a new issue