fix: return specified errors for VPP operations (#20640)

> Related issue: #20229

# 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] Added/updated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Jahziel Villasana-Espinoza 2024-07-22 17:14:54 -04:00 committed by GitHub
parent 06cbdeba36
commit b449ef35fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 56 additions and 5 deletions

View file

@ -741,7 +741,6 @@ team_settings:
assert.Empty(t, savedTeam.Config.MDM.MacOSSetup.BootstrapPackage.Value)
assert.False(t, savedTeam.Config.MDM.EnableDiskEncryption)
assert.Equal(t, filepath.Base(tmpFile.Name()), *savedTeam.Filename)
}
func TestBasicGlobalAndTeamGitOps(t *testing.T) {
@ -1152,7 +1151,7 @@ func TestTeamVPPAppsGitOps(t *testing.T) {
{"testdata/gitops/team_vpp_valid_app.yml", "", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_empty.yml", "", time.Now().Add(-24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_app.yml", "vpp token expired", time.Now().Add(-24 * time.Hour)},
{"testdata/gitops/team_vpp_valid_app.yml", "VPP token expired", time.Now().Add(-24 * time.Hour)},
{"testdata/gitops/team_vpp_invalid_app.yml", "app not available on vpp account", time.Now().Add(24 * time.Hour)},
}

View file

@ -292,7 +292,7 @@ func (svc *Service) InstallSoftwareTitle(ctx context.Context, hostID uint, softw
// request error
if fleet.IsNotFound(err) {
return &fleet.BadRequestError{
Message: "Software title has no package or VPP app added. Please add software package or VPP app to install.",
Message: "Couldn't install software. Software title is not available for install. Please add software package or App Store app to install.",
InternalErr: ctxerr.WrapWithData(
ctx, err, "couldn't find an installer or VPP app for software title",
map[string]any{"host_id": host.ID, "team_id": host.TeamID, "title_id": softwareTitleID},
@ -317,6 +317,15 @@ func (svc *Service) installSoftwareFromVPP(ctx context.Context, host *fleet.Host
}
}
config, err := svc.ds.AppConfig(ctx)
if err != nil {
return ctxerr.Wrap(ctx, err, "fetching config to check MDM status")
}
if !config.MDM.EnabledAndConfigured {
return fleet.NewUserMessageError(errors.New("Couldn't install. MDM is turned off. Please make sure that MDM is turned on to install App Store apps."), http.StatusUnprocessableEntity)
}
mdmConnected, err := svc.ds.IsHostConnectedToFleetMDM(ctx, host)
if err != nil {
return ctxerr.Wrapf(ctx, err, "checking MDM status for host %d", host.ID)

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
@ -16,6 +17,8 @@ import (
"github.com/fleetdm/fleet/v4/server/mdm/apple/vpp"
)
// getVPPToken returns the base64 encoded VPP token, ready for use in requests to Apple's VPP API.
// It returns an error if the token is expired.
func (svc *Service) getVPPToken(ctx context.Context) (string, error) {
configMap, err := svc.ds.GetAllMDMConfigAssetsByName(ctx, []fleet.MDMAssetName{fleet.MDMAssetVPPToken})
if err != nil {
@ -44,7 +47,7 @@ func (svc *Service) getVPPToken(ctx context.Context) (string, error) {
}
if time.Now().After(exp) {
return "", ctxerr.Errorf(ctx, "vpp token expired on %s", exp.String())
return "", fleet.NewUserMessageError(errors.New("Couldn't install. VPP token expired."), http.StatusUnprocessableEntity)
}
return vppTokenData.Token, nil

View file

@ -11485,3 +11485,29 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
require.NoError(t, err)
assert.Empty(t, team1CalendarEvents)
}
func (s *integrationEnterpriseTestSuite) TestVPPAppsWithoutMDM() {
t := s.T()
ctx := context.Background()
// Create host
orbitHost := createOrbitEnrolledHost(t, "darwin", "nonmdm", s.ds)
// Create team and add host to team
var newTeamResp teamResponse
s.DoJSON("POST", "/api/latest/fleet/teams", &createTeamRequest{TeamPayload: fleet.TeamPayload{Name: ptr.String("Team 1")}}, http.StatusOK, &newTeamResp)
team := newTeamResp.Team
s.Do("POST", "/api/latest/fleet/hosts/transfer", &addHostsToTeamRequest{HostIDs: []uint{orbitHost.ID}, TeamID: &team.ID}, http.StatusOK)
// Add an app so that we don't get a not found error
app, err := s.ds.InsertVPPAppWithTeam(ctx, &fleet.VPPApp{
Name: "App " + t.Name(),
BundleIdentifier: "bid_" + t.Name(),
AdamID: "adam_" + t.Name(),
}, &team.ID)
require.NoError(t, err)
r := s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/install/%d", orbitHost.ID, app.TitleID), &installSoftwareRequest{}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(r.Body), "Couldn't install. MDM is turned off. Please make sure that MDM is turned on to install App Store apps.")
}

View file

@ -9798,10 +9798,24 @@ func (s *integrationMDMTestSuite) TestVPPApps() {
errTitleID := listSw.SoftwareTitles[1].ID
// attempt to install a VPP app on the non-MDM enrolled host
installResp := installSoftwareResponse{}
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/install/%d", orbitHost.ID, titleID), &installSoftwareRequest{}, http.StatusBadRequest, &installResp)
// Spoof an expired VPP token and attempt to install VPP app
tokenJSON = fmt.Sprintf(`{"expDate":"%s","token":"%s","orgName":"%s"}`, "2020-06-24T15:50:50+0000", token, orgName)
s.uploadDataViaForm("/api/latest/fleet/mdm/apple/vpp_token", "token", "token.vpptoken", []byte(base64.StdEncoding.EncodeToString([]byte(tokenJSON))), http.StatusAccepted, "")
r := s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/install/%d", mdmHost.ID, errTitleID), &installSoftwareRequest{}, http.StatusUnprocessableEntity)
require.Contains(t, extractServerErrorText(r.Body), "VPP token expired")
// Put a valid token back in
tokenJSON = fmt.Sprintf(`{"expDate":"%s","token":"%s","orgName":"%s"}`, "2050-06-24T15:50:50+0000", token, orgName)
s.uploadDataViaForm("/api/latest/fleet/mdm/apple/vpp_token", "token", "token.vpptoken", []byte(base64.StdEncoding.EncodeToString([]byte(tokenJSON))), http.StatusAccepted, "")
// Attempt to install non-existent app
r = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/install/%d", mdmHost.ID, 99999), &installSoftwareRequest{}, http.StatusBadRequest)
require.Contains(t, extractServerErrorText(r.Body), "Couldn't install software. Software title is not available for install. Please add software package or App Store app to install.")
// Trigger install to the host
installResp = installSoftwareResponse{}
s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/install/%d", mdmHost.ID, errTitleID), &installSoftwareRequest{}, http.StatusAccepted, &installResp)