Allow team_id=0 to signal "No Team" in os_versions (#20272)

for #20150

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Roberto Dip 2024-07-10 10:12:05 -03:00 committed by GitHub
parent 7b20060350
commit 886ab9098d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 40 additions and 11 deletions

1
changes/20150-no-team Normal file
View file

@ -0,0 +1 @@
* Fixed a bug causing "No Team" os versions to display the wrong number

View file

@ -4,7 +4,6 @@ import endpoints from "utilities/endpoints";
import { IOperatingSystemVersion } from "interfaces/operating_system";
import { Platform } from "interfaces/platform";
import { buildQueryStringFromParams } from "utilities/url";
import { API_NO_TEAM_ID } from "interfaces/team";
// TODO: add platforms to this constant as new ones are supported
export const OS_VERSIONS_API_SUPPORTED_PLATFORMS = [
@ -72,6 +71,7 @@ export const getOSVersions = ({
const params: IGetOSVersionsRequestQueryParams = {
platform,
team_id: teamId,
os_name,
os_version,
order_key,
@ -80,10 +80,6 @@ export const getOSVersions = ({
per_page,
};
if (teamId !== API_NO_TEAM_ID) {
params.team_id = teamId;
}
const queryString = buildQueryStringFromParams(params);
if (queryString) path += `?${queryString}`;

View file

@ -4665,6 +4665,8 @@ func (ds *Datastore) executeOSVersionQuery(ctx context.Context, teamFilter *flee
args := []interface{}{aggregatedStatsTypeOSVersions}
switch {
case teamFilter != nil && teamFilter.TeamID != nil:
// Aggregated stats for os versions are stored by team id with 0 representing
// no team or the all teams if global_stats is true.
query += " AND id = ? AND global_stats = ?"
args = append(args, *teamFilter.TeamID, false)
case teamFilter != nil:

View file

@ -1910,12 +1910,15 @@ func (svc *Service) OSVersions(ctx context.Context, teamID *uint, platform *stri
if err := svc.authz.Authorize(ctx, &fleet.AuthzSoftwareInventory{TeamID: teamID}, fleet.ActionRead); err != nil {
return nil, count, nil, err
}
exists, err := svc.ds.TeamExists(ctx, *teamID)
if err != nil {
return nil, count, nil, ctxerr.Wrap(ctx, err, "checking if team exists")
} else if !exists {
return nil, count, nil, fleet.NewInvalidArgumentError("team_id", fmt.Sprintf("team %d does not exist", *teamID)).
WithStatus(http.StatusNotFound)
if *teamID != 0 {
exists, err := svc.ds.TeamExists(ctx, *teamID)
if err != nil {
return nil, count, nil, ctxerr.Wrap(ctx, err, "checking if team exists")
} else if !exists {
return nil, count, nil, fleet.NewInvalidArgumentError("team_id", fmt.Sprintf("team %d does not exist", *teamID)).
WithStatus(http.StatusNotFound)
}
}
}

View file

@ -8991,6 +8991,15 @@ func (s *integrationTestSuite) TestOSVersions() {
require.Equal(t, 4, osVersionsResp.Count)
require.False(t, osVersionsResp.Meta.HasNextResults)
require.True(t, osVersionsResp.Meta.HasPreviousResults)
// same results with team_id=0
s.DoJSON("GET", "/api/latest/fleet/os_versions", nil, http.StatusOK, &osVersionsResp, "page", "1", "per_page", "2", "team_id", "0")
require.Len(t, osVersionsResp.OSVersions, 2)
require.Equal(t, "macOS 13.2.1", osVersionsResp.OSVersions[0].Name)
require.Equal(t, "macOS 14.1.2", osVersionsResp.OSVersions[1].Name)
require.Equal(t, 4, osVersionsResp.Count)
require.False(t, osVersionsResp.Meta.HasNextResults)
require.True(t, osVersionsResp.Meta.HasPreviousResults)
}
func (s *integrationTestSuite) TestPingEndpoints() {

View file

@ -3956,6 +3956,24 @@ func (s *integrationEnterpriseTestSuite) TestOSVersions() {
"GET", fmt.Sprintf("/api/latest/fleet/os_versions/%d", osinfo.OSVersionID), nil, http.StatusForbidden, &osVersionResp, "team_id",
"99999",
)
// team user doesn't have acess to "no team"
osVersionsResp = osVersionsResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/os_versions"), nil, http.StatusForbidden, &osVersionsResp, "team_id", "0")
require.Len(t, osVersionsResp.OSVersions, 0)
// team_id=0 is supported and returns results for hosts in "no team"
s.token = getTestAdminToken(t, s.server)
// no hosts, the results are empty
osVersionsResp = osVersionsResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/os_versions"), nil, http.StatusOK, &osVersionsResp, "team_id", "0")
require.Len(t, osVersionsResp.OSVersions, 0)
osVersionsResp = osVersionsResponse{}
// move the host to "no team" and update the stats
require.NoError(t, s.ds.AddHostsToTeam(context.Background(), nil, []uint{hosts[0].ID}))
require.NoError(t, s.ds.UpdateOSVersions(context.Background()))
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/os_versions"), nil, http.StatusOK, &osVersionsResp, "team_id", "0")
require.Len(t, osVersionsResp.OSVersions, 1)
}
func (s *integrationEnterpriseTestSuite) TestMDMNotConfiguredEndpoints() {