add activity items when a Windows host turns MDM on (#12635)

For #12427, and its sub-tasks #12288 and #12612



![image](https://github.com/fleetdm/fleet/assets/4419992/b4c019dd-fbd3-4c1d-a2ad-a0bb4ebac817)
This commit is contained in:
Roberto Dip 2023-07-06 15:33:40 -03:00 committed by GitHub
parent 100b211ba5
commit 2b4798c4ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 123 additions and 9 deletions

View file

@ -553,6 +553,7 @@ This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
- "installed_from_dep": Whether the host was enrolled via DEP.
- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility.
#### Example
@ -560,7 +561,8 @@ This activity contains the following fields:
{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
"installed_from_dep": true
"installed_from_dep": true,
"mdm_platform": "apple"
}
```

View file

@ -82,6 +82,7 @@ export interface IActivityDetails {
host_display_names?: string[];
host_ids?: number[];
installed_from_dep?: boolean;
mdm_platform?: "microsoft" | "apple";
minimum_version?: string;
deadline?: string;
profile_name?: string;

View file

@ -708,4 +708,65 @@ describe("Activity Feed", () => {
expect(screen.queryByText("baz")).toBeNull();
expect(screen.getByText("Alphas", { exact: false })).toBeInTheDocument();
});
it("renders a 'mdm_enrolled' type for apple if mdm_platform is not provided", () => {
const activity = createMockActivity({
type: ActivityType.MdmEnrolled,
details: {
host_serial: "ABCD",
},
});
render(<ActivityItem activity={activity} isPremiumTier />);
expect(
screen.getByText((content, node) => {
return (
node?.innerHTML ===
"<b>Test User </b>An end user turned on MDM features for a host with serial number <b>ABCD (manual)</b>."
);
})
).toBeInTheDocument();
});
it("renders a 'mdm_enrolled' type for apple with all details provided", () => {
const activity = createMockActivity({
type: ActivityType.MdmEnrolled,
details: {
host_serial: "ABCD",
installed_from_dep: true,
mdm_platform: "apple",
},
});
render(<ActivityItem activity={activity} isPremiumTier />);
expect(
screen.getByText((content, node) => {
return (
node?.innerHTML ===
"<b>Test User </b>An end user turned on MDM features for a host with serial number <b>ABCD (automatic)</b>."
);
})
).toBeInTheDocument();
});
it("renders a 'mdm_enrolled' type activity for windows hosts.", () => {
const activity = createMockActivity({
type: ActivityType.MdmEnrolled,
details: {
mdm_platform: "microsoft",
host_display_name: "ABCD",
},
});
render(<ActivityItem activity={activity} isPremiumTier />);
expect(
screen.getByText((content, node) => {
console.log(node?.innerHTML);
return (
node?.innerHTML ===
"<b>Test User </b>Mobile device management (MDM) was turned on for <b>ABCD (manual)</b>."
);
})
).toBeInTheDocument();
});
});

View file

@ -215,6 +215,17 @@ const TAGGED_TEMPLATES = {
);
},
mdmEnrolled: (activity: IActivity) => {
if (activity.details?.mdm_platform === "microsoft") {
return (
<>
Mobile device management (MDM) was turned on for{" "}
<b>{activity.details?.host_display_name} (manual)</b>.
</>
);
}
// note: if mdm_platform is missing, we assume this is Apple MDM for backwards
// compatibility
return (
<>
An end user turned on MDM features for a host with serial number{" "}

View file

@ -692,6 +692,7 @@ type ActivityTypeMDMEnrolled struct {
HostSerial string `json:"host_serial"`
HostDisplayName string `json:"host_display_name"`
InstalledFromDEP bool `json:"installed_from_dep"`
MDMPlatform string `json:"mdm_platform"`
}
func (a ActivityTypeMDMEnrolled) ActivityName() string {
@ -703,10 +704,12 @@ func (a ActivityTypeMDMEnrolled) Documentation() (activity string, details strin
`This activity contains the following fields:
- "host_serial": Serial number of the host.
- "host_display_name": Display name of the host.
- "installed_from_dep": Whether the host was enrolled via DEP.`, `{
- "installed_from_dep": Whether the host was enrolled via DEP.
- "mdm_platform": Used to distinguish between Apple and Microsoft enrollments. Can be "apple", "microsoft" or not present. If missing, this value is treated as "apple" for backwards compatibility.`, `{
"host_serial": "C08VQ2AXHT96",
"host_display_name": "MacBookPro16,1 (C08VQ2AXHT96)",
"installed_from_dep": true
"installed_from_dep": true,
"mdm_platform": "apple"
}`
}

View file

@ -7,6 +7,11 @@ import (
"time"
)
const (
MDMPlatformApple = "apple"
MDMPlatformMicrosoft = "microsoft"
)
type AppleMDM struct {
CommonName string `json:"common_name"`
SerialNumber string `json:"serial_number"`

View file

@ -2211,6 +2211,7 @@ func (svc *MDMAppleCheckinAndCommandService) Authenticate(r *mdm.Request, m *mdm
HostSerial: info.HardwareSerial,
HostDisplayName: info.DisplayName,
InstalledFromDEP: info.InstalledFromDEP,
MDMPlatform: fleet.MDMPlatformApple,
})
}

View file

@ -972,6 +972,7 @@ func TestMDMAuthenticate(t *testing.T) {
require.Equal(t, serial, a.HostSerial)
require.Equal(t, a.HostDisplayName, fmt.Sprintf("%s (%s)", model, serial))
require.False(t, a.InstalledFromDEP)
require.Equal(t, fleet.MDMPlatformApple, a.MDMPlatform)
return nil
}

View file

@ -925,7 +925,7 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
require.JSONEq(
t,
fmt.Sprintf(
`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true}`,
`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`,
devices[0].SerialNumber, devices[0].Model, devices[0].SerialNumber,
),
string(*activity.Details),
@ -1068,8 +1068,8 @@ func (s *integrationMDMTestSuite) TestAppleMDMDeviceEnrollment() {
}
}
require.Len(t, details, 2)
require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), string(*details[len(details)-2]))
require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), string(*details[len(details)-1]))
require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceA.SerialNumber, mdmDeviceA.Model, mdmDeviceA.SerialNumber), string(*details[len(details)-2]))
require.JSONEq(t, fmt.Sprintf(`{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": false, "mdm_platform": "apple"}`, mdmDeviceB.SerialNumber, mdmDeviceB.Model, mdmDeviceB.SerialNumber), string(*details[len(details)-1]))
// set an enroll secret
var applyResp applyEnrollSecretSpecResponse
@ -5414,6 +5414,17 @@ func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequest() {
require.True(t, s.isXMLTagContentPresent("TokenType", resSoapMsg))
require.True(t, s.isXMLTagContentPresent("RequestID", resSoapMsg))
require.True(t, s.isXMLTagContentPresent("BinarySecurityToken", resSoapMsg))
// Checking if an activity was created for the enrollment
s.lastActivityOfTypeMatches(
fleet.ActivityTypeMDMEnrolled{}.ActivityName(),
`{
"mdm_platform": "microsoft",
"host_serial": "",
"installed_from_dep": false,
"host_display_name": "DESKTOP-0C89RC0"
}`,
0)
}
func (s *integrationMDMTestSuite) TestInvalidRequestSecurityTokenRequestWithMissingAdditionalContext() {

View file

@ -819,10 +819,15 @@ func (svc *Service) GetMDMWindowsEnrollResponse(ctx context.Context, secTokenMsg
return nil, ctxerr.Wrap(ctx, err, "creation of RequestSecurityTokenResponseCollection message")
}
// RequestSecurityTokenResponseCollection message is ready
// The identity and provisioning information will be sent to the Windows MDM Enrollment Client
// RequestSecurityTokenResponseCollection message is ready. The identity
// and provisioning information will be sent to the Windows MDM
// Enrollment Client
// But before doing that, let's save the device information to the list of MDM enrolled MDM devices
// But before doing that, let's save the device information to the list
// of MDM enrolled MDM devices
//
// This method also creates the relevant enrollment activity as it has
// access to the device information.
err = svc.storeWindowsMDMEnrolledDevice(ctx, secTokenMsg)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "enrolled device information cannot be stored")
@ -1004,6 +1009,19 @@ func (svc *Service) storeWindowsMDMEnrolledDevice(ctx context.Context, secTokenM
return err
}
err = svc.ds.NewActivity(ctx, nil, &fleet.ActivityTypeMDMEnrolled{
HostDisplayName: reqDeviceName,
MDMPlatform: fleet.MDMPlatformMicrosoft,
})
if err != nil {
// only logging, the device is enrolled at this point, and we
// wouldn't want to fail the request because there was a problem
// creating an activity feed item.
logging.WithExtras(logging.WithNoUser(ctx),
"msg", "failed to generate windows MDM enrolled activity",
)
}
return nil
}