Match Windows Fleet maintained apps by similar name (#40544)
**Related issue:** Resolves #37802 Attempt to fix Windows FMAs not showing up as available when the software titles they match to have upgrade codes. Since we don't surface upgrade codes in the `fleet_maintained_apps` table and matching exactly by name could miss some cases, this fix uses `team_titles.name LIKE CONCAT(LEAST(fma.name, fma.unique_identifier), '%')`. Note the LEAST there is only for the "Box Drive" app which has a longer name than unique_identifier, and just compares the strings and not their length. This isn't optimal for performance or correctness, but it only checks with titles already available to the team as installers so it shouldn't be terrible. Until upgrade_code is surfaced in the `fleet_maintained_apps` table this should be sufficient. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- Fixed a bug where Fleet maintained apps for Windows won't show as available in the list when they actually are.
|
||||
@@ -47,7 +47,7 @@ ON DUPLICATE KEY UPDATE
|
||||
const teamFMATitlesJoin = `
|
||||
team_titles.id software_title_id FROM fleet_maintained_apps fma
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT st.id, st.unique_identifier
|
||||
SELECT DISTINCT st.id, st.unique_identifier, st.name, si.platform
|
||||
FROM software_titles st
|
||||
LEFT JOIN
|
||||
software_installers si
|
||||
@@ -63,7 +63,16 @@ const teamFMATitlesJoin = `
|
||||
AND vat.platform = va.platform
|
||||
AND vat.global_or_team_id = ?
|
||||
WHERE si.id IS NOT NULL OR vat.id IS NOT NULL
|
||||
) team_titles ON team_titles.unique_identifier = fma.unique_identifier`
|
||||
) team_titles
|
||||
ON team_titles.unique_identifier = fma.unique_identifier
|
||||
-- pattern match fma name to a similar title name, since upgrade_code is not surfaced in fma table
|
||||
OR (
|
||||
team_titles.platform = fma.platform
|
||||
AND fma.platform = 'windows'
|
||||
-- Box Drive is the only FMA at the point of writing this where unique_identifier is shorter than name
|
||||
AND team_titles.name LIKE CONCAT(LEAST(fma.name, fma.unique_identifier), '%')
|
||||
)
|
||||
`
|
||||
|
||||
func (ds *Datastore) GetMaintainedAppByID(ctx context.Context, appID uint, teamID *uint) (*fleet.MaintainedApp, error) {
|
||||
stmt := `SELECT fma.id, fma.name, fma.platform, fma.unique_identifier, fma.slug, `
|
||||
|
||||
@@ -24,6 +24,7 @@ func TestMaintainedApps(t *testing.T) {
|
||||
{"ListAndGetAvailableApps", testListAndGetAvailableApps},
|
||||
{"SyncAndRemoveApps", testSyncAndRemoveApps},
|
||||
{"GetMaintainedAppBySlug", testGetMaintainedAppBySlug},
|
||||
{"ListAvailableAppsWindows", testListAvailableAppsWindows},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -542,3 +543,89 @@ func testGetMaintainedAppBySlug(t *testing.T, ds *Datastore) {
|
||||
TitleID: nil,
|
||||
}, gotApp)
|
||||
}
|
||||
|
||||
func testListAvailableAppsWindows(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "Team 1"})
|
||||
require.NoError(t, err)
|
||||
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
||||
|
||||
maintained1, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
|
||||
Name: "Maintained1",
|
||||
Slug: "maintained1",
|
||||
Platform: "windows",
|
||||
UniqueIdentifier: "Maintained1 (MSI)",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
maintained2, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
|
||||
Name: "Maintained2",
|
||||
Slug: "maintained2",
|
||||
Platform: "darwin",
|
||||
UniqueIdentifier: "com.foo",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedApps := []fleet.MaintainedApp{
|
||||
{
|
||||
ID: maintained1.ID,
|
||||
Name: maintained1.Name,
|
||||
Platform: maintained1.Platform,
|
||||
Slug: "maintained1",
|
||||
},
|
||||
{
|
||||
ID: maintained2.ID,
|
||||
Name: maintained2.Name,
|
||||
Platform: maintained2.Platform,
|
||||
Slug: "maintained2",
|
||||
},
|
||||
}
|
||||
apps, _, err := ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
require.Nil(t, apps[0].TitleID)
|
||||
require.Nil(t, apps[1].TitleID)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
// upload an installer that will create a title with a similar name, but with
|
||||
// an upgrade code so that unique identifier doesn't match
|
||||
_, titleID, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
Title: "Maintained1 (MSI)",
|
||||
UpgradeCode: "{UPGRADE-CODE}",
|
||||
Source: "programs",
|
||||
StorageID: "storageid1",
|
||||
Filename: "maintained1.msi",
|
||||
Extension: "msi",
|
||||
Platform: "windows",
|
||||
Version: "1.0",
|
||||
UserID: user.ID,
|
||||
TeamID: &team1.ID,
|
||||
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
||||
FleetMaintainedAppID: ptr.Uint(maintained1.ID),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// create a pkg installer that should not match by similar name
|
||||
_, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
Title: "Maintained2 ",
|
||||
BundleIdentifier: "Maintained2.ShallNotBeMatched",
|
||||
Source: "apps",
|
||||
StorageID: "storageid2",
|
||||
Filename: "maintained2.pkg",
|
||||
Extension: "pkg",
|
||||
Platform: "darwin",
|
||||
Version: "1.0",
|
||||
UserID: user.ID,
|
||||
TeamID: &team1.ID,
|
||||
ValidatedLabels: &fleet.LabelIdentsWithScope{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// the windows app should be found using using name, because the existing software title has an upgrade code
|
||||
apps, _, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
require.NotNil(t, apps[0].TitleID)
|
||||
require.Equal(t, titleID, *apps[0].TitleID)
|
||||
// the darwin app should not be matched by name
|
||||
require.Nil(t, apps[1].TitleID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user