mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
remove default'd available_for_install (#30516)
This was added to support the "All Software" when listing software on the host. Fixes #30188 - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes - [x] Added/updated automated tests - [x] Manual QA for all new/changed functionality - [x] For unreleased bug fixes in a release candidate, confirmed that the fix is not expected to adversely impact load test results or alerted the release DRI if additional load testing is needed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an option to explicitly exclude software available for install from host software listings. * **Bug Fixes** * Improved accuracy of software inventory results when filtering by availability for install. * **Tests** * Added a test to verify exclusion of available-for-install software when the relevant option is set. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ian Littman <iansltx@gmail.com>
This commit is contained in:
parent
6aa3455634
commit
6d5ac49c74
6 changed files with 75 additions and 13 deletions
|
|
@ -970,7 +970,10 @@ const HostDetailsPage = ({
|
|||
softwareUpdatedAt={host.software_updated_at}
|
||||
isSoftwareEnabled={featuresConfig?.enable_software_inventory}
|
||||
router={router}
|
||||
queryParams={parseHostSoftwareQueryParams(location.query)}
|
||||
queryParams={{
|
||||
...parseHostSoftwareQueryParams(location.query),
|
||||
include_available_for_install: false,
|
||||
}}
|
||||
pathname={location.pathname}
|
||||
onShowSoftwareDetails={setSelectedSoftwareDetails}
|
||||
hostTeamId={host.team_id || 0}
|
||||
|
|
@ -1013,7 +1016,10 @@ const HostDetailsPage = ({
|
|||
softwareUpdatedAt={host.software_updated_at}
|
||||
isSoftwareEnabled={featuresConfig?.enable_software_inventory}
|
||||
router={router}
|
||||
queryParams={parseHostSoftwareQueryParams(location.query)}
|
||||
queryParams={{
|
||||
...parseHostSoftwareQueryParams(location.query),
|
||||
include_available_for_install: false,
|
||||
}}
|
||||
pathname={location.pathname}
|
||||
onShowSoftwareDetails={setSelectedSoftwareDetails}
|
||||
hostTeamId={host.team_id || 0}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,18 @@ export interface ITableSoftware extends Omit<ISoftware, "vulnerabilities"> {
|
|||
vulnerabilities: string[]; // for client-side search purposes, we only want an array of cve strings
|
||||
}
|
||||
|
||||
interface HostSoftwareQueryParams
|
||||
extends ReturnType<typeof parseHostSoftwareQueryParams> {
|
||||
include_available_for_install?: boolean;
|
||||
}
|
||||
|
||||
interface IHostSoftwareProps {
|
||||
/** This is the host id or the device token */
|
||||
id: number | string;
|
||||
platform: HostPlatform;
|
||||
softwareUpdatedAt?: string;
|
||||
router: InjectedRouter;
|
||||
queryParams: ReturnType<typeof parseHostSoftwareQueryParams>;
|
||||
queryParams: HostSoftwareQueryParams;
|
||||
pathname: string;
|
||||
hostTeamId: number;
|
||||
onShowSoftwareDetails: (software: IHostSoftware) => void;
|
||||
|
|
@ -289,7 +294,12 @@ const HostSoftware = ({
|
|||
searchQuery={queryParams.query}
|
||||
page={queryParams.page}
|
||||
pagePath={pathname}
|
||||
vulnFilters={getSoftwareVulnFiltersFromQueryParams(queryParams)}
|
||||
vulnFilters={getSoftwareVulnFiltersFromQueryParams({
|
||||
vulnerable: queryParams.vulnerable,
|
||||
exploit: queryParams.exploit,
|
||||
min_cvss_score: queryParams.min_cvss_score,
|
||||
max_cvss_score: queryParams.max_cvss_score,
|
||||
})}
|
||||
onAddFiltersClick={toggleSoftwareFiltersModal}
|
||||
pathPrefix={pathname}
|
||||
// for my device software details modal toggling
|
||||
|
|
@ -301,7 +311,12 @@ const HostSoftware = ({
|
|||
<SoftwareFiltersModal
|
||||
onExit={toggleSoftwareFiltersModal}
|
||||
onSubmit={onApplyVulnFilters}
|
||||
vulnFilters={getSoftwareVulnFiltersFromQueryParams(queryParams)}
|
||||
vulnFilters={getSoftwareVulnFiltersFromQueryParams({
|
||||
vulnerable: queryParams.vulnerable,
|
||||
exploit: queryParams.exploit,
|
||||
min_cvss_score: queryParams.min_cvss_score,
|
||||
max_cvss_score: queryParams.max_cvss_score,
|
||||
})}
|
||||
isPremiumTier={isPremiumTier || false}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ export interface IHostSoftwareQueryParams extends QueryParams {
|
|||
order_key: string;
|
||||
order_direction: "asc" | "desc";
|
||||
available_for_install?: boolean;
|
||||
include_available_for_install?: boolean;
|
||||
vulnerable?: boolean;
|
||||
min_cvss_score?: number;
|
||||
max_cvss_score?: number;
|
||||
|
|
|
|||
|
|
@ -270,10 +270,12 @@ type HostSoftwareTitleListOptions struct {
|
|||
// AvailableForInstall.
|
||||
SelfServiceOnly bool `query:"self_service,optional"`
|
||||
|
||||
// IncludeAvailableForInstall is not a query argument, it is set in the
|
||||
// service layer to indicate to the datastore if software available for
|
||||
// install (but not currently installed on the host) should be returned.
|
||||
IncludeAvailableForInstall bool
|
||||
IncludeAvailableForInstall bool `query:"include_available_for_install,optional"`
|
||||
// IncludeAvailableForInstall was exposed as a query string parameter
|
||||
// In order not to introduce a breaking change we have to mark this parameter as optional.
|
||||
// However, instead of using *bool and modifying a lot of downstream code and tests
|
||||
// Use this indicator
|
||||
IncludeAvailableForInstallExplicitlySet bool
|
||||
|
||||
// OnlyAvailableForInstall is set via a query argument that limits the
|
||||
// returned software titles to only those that are available for install on
|
||||
|
|
|
|||
|
|
@ -2780,6 +2780,31 @@ type getHostSoftwareResponse struct {
|
|||
|
||||
func (r getHostSoftwareResponse) Error() error { return r.Err }
|
||||
|
||||
func (r getHostSoftwareRequest) DecodeRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||
type defaultDecodeRequest struct {
|
||||
ID uint `url:"id"`
|
||||
fleet.HostSoftwareTitleListOptions
|
||||
}
|
||||
|
||||
defaultDecoder := makeDecoder(defaultDecodeRequest{})
|
||||
decoded, err := defaultDecoder(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := decoded.(*defaultDecodeRequest)
|
||||
queryParams := req.URL.Query()
|
||||
_, wasIncludeAvailableForInstallSet := queryParams["include_available_for_install"]
|
||||
result.HostSoftwareTitleListOptions.IncludeAvailableForInstallExplicitlySet = wasIncludeAvailableForInstallSet
|
||||
|
||||
finalResult := getHostSoftwareRequest{
|
||||
ID: result.ID,
|
||||
HostSoftwareTitleListOptions: result.HostSoftwareTitleListOptions,
|
||||
}
|
||||
|
||||
return &finalResult, nil
|
||||
}
|
||||
|
||||
func getHostSoftwareEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (fleet.Errorer, error) {
|
||||
req := request.(*getHostSoftwareRequest)
|
||||
res, meta, err := svc.ListHostSoftware(ctx, req.ID, req.HostSoftwareTitleListOptions)
|
||||
|
|
@ -2793,10 +2818,12 @@ func getHostSoftwareEndpoint(ctx context.Context, request interface{}, svc fleet
|
|||
}
|
||||
|
||||
func (svc *Service) ListHostSoftware(ctx context.Context, hostID uint, opts fleet.HostSoftwareTitleListOptions) ([]*fleet.HostSoftwareWithInstaller, *fleet.PaginationMetadata, error) {
|
||||
// if the request is token-authenticated ("My device" page), we don't include
|
||||
// software that is not installed but for which there's an installer
|
||||
// available for that host (unless the request filters for self-service
|
||||
// software only).
|
||||
// When accessed via "My device", we default to only showing inventory (excluding software available for install
|
||||
// but not in inventory), unless we're asked to filter to self-service software only.
|
||||
//
|
||||
// Otherwise (e.g. host software UI within Fleet's admin interface), the default is to show both installed and
|
||||
// available-for-install software, to maintain existing API behavior. This behavior can be explicitly overridden
|
||||
// if needed (see opts.IncludeAvailableForInstallExplicitlySet).
|
||||
var includeAvailableForInstall bool
|
||||
|
||||
var host *fleet.Host
|
||||
|
|
@ -2830,6 +2857,10 @@ func (svc *Service) ListHostSoftware(ctx context.Context, hostID uint, opts flee
|
|||
return nil, nil, ctxerr.Wrap(ctx, err, "checking mdm enrollment status")
|
||||
}
|
||||
|
||||
if opts.IncludeAvailableForInstallExplicitlySet {
|
||||
includeAvailableForInstall = opts.IncludeAvailableForInstall
|
||||
}
|
||||
|
||||
// cursor-based pagination is not supported
|
||||
opts.ListOptions.After = ""
|
||||
// custom ordering is not supported, always by name (but asc/desc is configurable)
|
||||
|
|
|
|||
|
|
@ -10615,6 +10615,13 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftware() {
|
|||
require.True(t, *getHostSw.Software[2].SoftwarePackage.SelfService)
|
||||
require.Nil(t, getHostSw.Software[2].Status)
|
||||
|
||||
// user authenticated endpoint, but explicitly request to not include available for install software
|
||||
getHostSw = getHostSoftwareResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=false", host.ID), nil, http.StatusOK, &getHostSw)
|
||||
require.Len(t, getHostSw.Software, 2) // foo and bar
|
||||
require.Equal(t, getHostSw.Software[0].Name, "bar")
|
||||
require.Equal(t, getHostSw.Software[1].Name, "foo")
|
||||
|
||||
// only the installer is returned for self-service only
|
||||
getHostSw = getHostSoftwareResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw, "self_service", "true")
|
||||
|
|
|
|||
Loading…
Reference in a new issue