Skip to content

Commit

Permalink
Merge branch 'main' into feat-automatic-migrate-windows-hosts
Browse files Browse the repository at this point in the history
  • Loading branch information
mna authored Dec 3, 2024
2 parents 7d3b11a + 941713d commit 15a8214
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 15 deletions.
16 changes: 8 additions & 8 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ go.mod @fleetdm/go
#
# (see website/config/custom.js for DRIs of other paths not listed here)
##############################################################################################
/docs @rachaelshaw
/docs/REST\ API/rest-api.md @rachaelshaw # « REST API reference documentation
/docs/Contributing/API-for-contributors.md @rachaelshaw # « Advanced / contributors-only API reference documentation
/docs @rachaelshaw @noahtalerman
/docs/REST\ API/rest-api.md @rachaelshaw @noahtalerman # « REST API reference documentation
/docs/Contributing/API-for-contributors.md @rachaelshaw @noahtalerman # « Advanced / contributors-only API reference documentation
/schema @eashaw # « Data tables (osquery/fleetd schema) documentation
/render.yaml @edwardsb

Expand All @@ -88,15 +88,15 @@ go.mod @fleetdm/go
/handbook/README.md @mikermcneil
/handbook/company/open-positions.yml @sampfluger88
#/handbook/company/product-groups.md 🤡 Covered in custom.js
/handbook/finance/README.md @sampfluger88
/handbook/finance/finance.rituals.yml @sampfluger88
/handbook/finance/README.md @sampfluger88
/handbook/finance/finance.rituals.yml @sampfluger88
/handbook/digital-experience/security.md @sampfluger88
/handbook/digital-experience @sampfluger88
/handbook/customer-success @sampfluger88
/handbook/digital-experience @sampfluger88
/handbook/customer-success @sampfluger88
/handbook/demand @sampfluger88
#/handbook/engineering 🤡 Covered in custom.js
/handbook/sales @sampfluger88
#/handbook/product-design 🤡 Covered in custom.js
#/handbook/product-design 🤡 Covered in custom.js

##############################################################################################
# 🌐 GitHub issue templates
Expand Down
1 change: 1 addition & 0 deletions changes/24288-mdm-gitops-role
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed breaking with gitops user role running `fleetctl gitops` command when MDM is enabled.
4 changes: 2 additions & 2 deletions cmd/fleetctl/gitops.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,12 @@ func checkABMTeamAssignments(config *spec.GitOps, fleetClient *service.Client) (
return nil, false, false, errors.New(fleet.AppleABMDefaultTeamDeprecatedMessage)
}

abmToks, err := fleetClient.ListABMTokens()
abmToks, err := fleetClient.CountABMTokens()
if err != nil {
return nil, false, false, err
}

if hasLegacyConfig && len(abmToks) > 1 {
if hasLegacyConfig && abmToks > 1 {
return nil, false, false, errors.New(fleet.AppleABMDefaultTeamDeprecatedMessage)
}

Expand Down
12 changes: 12 additions & 0 deletions cmd/fleetctl/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,9 @@ func TestGitOpsBasicGlobalAndTeam(t *testing.T) {
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
return []*fleet.ABMToken{}, nil
}
ds.GetABMTokenCountFunc = func(ctx context.Context) (int, error) {
return 0, nil
}
ds.DeleteSetupExperienceScriptFunc = func(ctx context.Context, teamID *uint) error {
return nil
}
Expand Down Expand Up @@ -1815,6 +1818,9 @@ func TestGitOpsFullGlobalAndTeam(t *testing.T) {
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
return []*fleet.ABMToken{}, nil
}
ds.GetABMTokenCountFunc = func(ctx context.Context) (int, error) {
return 0, nil
}

apnsCert, apnsKey, err := mysql.GenerateTestCertBytes()
require.NoError(t, err)
Expand Down Expand Up @@ -2854,6 +2860,9 @@ software:
}
return []*fleet.ABMToken{{OrganizationName: "Fleet Device Management Inc."}, {OrganizationName: "Foo Inc."}}, nil
}
ds.GetABMTokenCountFunc = func(ctx context.Context) (int, error) {
return len(tt.tokens), nil
}

ds.TeamsSummaryFunc = func(ctx context.Context) ([]*fleet.TeamSummary, error) {
var res []*fleet.TeamSummary
Expand Down Expand Up @@ -3177,6 +3186,9 @@ software:
ds.ListABMTokensFunc = func(ctx context.Context) ([]*fleet.ABMToken, error) {
return []*fleet.ABMToken{{OrganizationName: "Fleet Device Management Inc."}, {OrganizationName: "Foo Inc."}}, nil
}
ds.GetABMTokenCountFunc = func(ctx context.Context) (int, error) {
return 1, nil
}

ds.TeamsSummaryFunc = func(ctx context.Context) ([]*fleet.TeamSummary, error) {
var res []*fleet.TeamSummary
Expand Down
16 changes: 16 additions & 0 deletions ee/server/service/mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,22 @@ func (svc *Service) ListABMTokens(ctx context.Context) ([]*fleet.ABMToken, error
return tokens, nil
}

func (svc *Service) CountABMTokens(ctx context.Context) (int, error) {
// Authorizing using the more general AppConfig object because:
// - this service method returns a count, which is not sensitive information
// - gitops role, which needs this info, is not authorized for AppleBM access (as of 2024/12/02)
if err := svc.authz.Authorize(ctx, &fleet.AppConfig{}, fleet.ActionRead); err != nil {
return 0, err
}

tokens, err := svc.ds.GetABMTokenCount(ctx)
if err != nil {
return 0, ctxerr.Wrap(ctx, err, "count ABM tokens")
}

return tokens, nil
}

func (svc *Service) UpdateABMTokenTeams(ctx context.Context, tokenID uint, macOSTeamID, iOSTeamID, iPadOSTeamID *uint) (*fleet.ABMToken, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleBM{}, fleet.ActionWrite); err != nil {
return nil, err
Expand Down
46 changes: 46 additions & 0 deletions ee/server/service/mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import (
"strings"
"testing"

"github.com/fleetdm/fleet/v4/server/authz"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm"
"github.com/fleetdm/fleet/v4/server/mdm/apple/mobileconfig"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -148,3 +151,46 @@ b1xn1jGQd/o0xFf9ojpDNy6vNojidQGHh6E3h0GYvxbnQmVNq5U=
// prevent static analysis tools from raising issues due to detection of
// private key in code.
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }

func TestCountABMTokensAuth(t *testing.T) {
t.Parallel()
ds := new(mock.Store)
ctx := context.Background()
authorizer, err := authz.NewAuthorizer()
require.NoError(t, err)
svc := Service{ds: ds, authz: authorizer}

ds.GetABMTokenCountFunc = func(ctx context.Context) (int, error) {
return 5, nil
}

t.Run("CountABMTokens", func(t *testing.T) {
cases := []struct {
desc string
user *fleet.User
shoudFailWithAuth bool
}{
{"no role", test.UserNoRoles, true},
{"gitops can read", test.UserGitOps, false},
{"maintainer can read", test.UserMaintainer, false},
{"observer can read", test.UserObserver, false},
{"observer+ can read", test.UserObserverPlus, false},
{"admin can read", test.UserAdmin, false},
{"tm1 gitops cannot read", test.UserTeamGitOpsTeam1, true},
{"tm1 maintainer can read", test.UserTeamMaintainerTeam1, false},
{"tm1 observer can read", test.UserTeamObserverTeam1, false},
{"tm1 observer+ can read", test.UserTeamObserverPlusTeam1, false},
{"tm1 admin can read", test.UserTeamAdminTeam1, false},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
ctx = test.UserContext(ctx, c.user)
count, err := svc.CountABMTokens(ctx)
checkAuthErr(t, c.shoudFailWithAuth, err)
if !c.shoudFailWithAuth {
assert.EqualValues(t, 5, count)
}
})
}
})
}
18 changes: 17 additions & 1 deletion server/datastore/mysql/apple_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6536,6 +6536,14 @@ func testMDMAppleGetAndUpdateABMToken(t *testing.T, ds *Datastore) {
tm3, err := ds.NewTeam(ctx, &fleet.Team{Name: "team3"})
require.NoError(t, err)

toks, err := ds.ListABMTokens(ctx)
require.NoError(t, err)
require.Empty(t, toks)

tokCount, err := ds.GetABMTokenCount(ctx)
require.NoError(t, err)
assert.EqualValues(t, 0, tokCount)

// create a token with an empty name and no team set, and another that will be unused
encTok := uuid.NewString()

Expand All @@ -6546,10 +6554,14 @@ func testMDMAppleGetAndUpdateABMToken(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.NotEmpty(t, t2.ID)

toks, err := ds.ListABMTokens(ctx)
toks, err = ds.ListABMTokens(ctx)
require.NoError(t, err)
require.Len(t, toks, 2)

tokCount, err = ds.GetABMTokenCount(ctx)
require.NoError(t, err)
assert.EqualValues(t, 2, tokCount)

// get that token
tok, err = ds.GetABMTokenByOrgName(ctx, "")
require.NoError(t, err)
Expand Down Expand Up @@ -6645,6 +6657,10 @@ func testMDMAppleGetAndUpdateABMToken(t *testing.T, ds *Datastore) {
require.Equal(t, uint(0), expTok.MacOSTeam.ID)
require.Equal(t, tm2.Name, expTok.IOSTeamName)
require.Equal(t, tm3.Name, expTok.IPadOSTeamName)

tokCount, err = ds.GetABMTokenCount(ctx)
require.NoError(t, err)
assert.EqualValues(t, 1, tokCount)
}

func testMDMAppleABMTokensTermsExpired(t *testing.T, ds *Datastore) {
Expand Down
3 changes: 3 additions & 0 deletions server/fleet/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,9 @@ type Service interface {
// ListABMTokens lists all the ABM tokens in Fleet.
ListABMTokens(ctx context.Context) ([]*ABMToken, error)

// CountABMTokens counts the ABM tokens in Fleet.
CountABMTokens(ctx context.Context) (int, error)

// UpdateABMTokenTeams updates the default macOS, iOS, and iPadOS team IDs for a given ABM token.
UpdateABMTokenTeams(ctx context.Context, tokenID uint, macOSTeamID, iOSTeamID, iPadOSTeamID *uint) (*ABMToken, error)

Expand Down
29 changes: 29 additions & 0 deletions server/service/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4480,6 +4480,35 @@ func (svc *Service) ListABMTokens(ctx context.Context) ([]*fleet.ABMToken, error
return nil, fleet.ErrMissingLicense
}

// //////////////////////////////////////////////////////////////////////////////
// Count ABM tokens endpoint
// //////////////////////////////////////////////////////////////////////////////

type countABMTokensResponse struct {
Err error `json:"error,omitempty"`
Count int `json:"count"`
}

func (r countABMTokensResponse) error() error { return r.Err }

func countABMTokensEndpoint(ctx context.Context, _ interface{}, svc fleet.Service) (errorer, error) {
tokenCount, err := svc.CountABMTokens(ctx)
if err != nil {
return &countABMTokensResponse{Err: err}, nil
}

return &countABMTokensResponse{Count: tokenCount}, nil
}

func (svc *Service) CountABMTokens(ctx context.Context) (int, error) {
// Automatic enrollment (ABM/ADE/DEP) is a feature that requires a license.
// skipauth: No authorization check needed due to implementation returning
// only license error.
svc.authz.SkipAuthorization(ctx)

return 0, fleet.ErrMissingLicense
}

////////////////////////////////////////////////////////////////////////////////
// Update ABM token teams endpoint
////////////////////////////////////////////////////////////////////////////////
Expand Down
8 changes: 4 additions & 4 deletions server/service/client_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ func (c *Client) GetAppleBM() (*fleet.AppleBM, error) {
return responseBody.AppleBM, err
}

func (c *Client) ListABMTokens() ([]*fleet.ABMToken, error) {
verb, path := "GET", "/api/latest/fleet/abm_tokens"
var responseBody listABMTokensResponse
func (c *Client) CountABMTokens() (int, error) {
verb, path := "GET", "/api/latest/fleet/abm_tokens/count"
var responseBody countABMTokensResponse
err := c.authenticatedRequestWithQuery(nil, verb, path, &responseBody, "")
return responseBody.Tokens, err
return responseBody.Count, err
}

// RequestAppleCSR requests a signed CSR from the Fleet server and returns the
Expand Down
1 change: 1 addition & 0 deletions server/service/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
ue.POST("/api/_version_/fleet/abm_tokens", uploadABMTokenEndpoint, uploadABMTokenRequest{})
ue.DELETE("/api/_version_/fleet/abm_tokens/{id:[0-9]+}", deleteABMTokenEndpoint, deleteABMTokenRequest{})
ue.GET("/api/_version_/fleet/abm_tokens", listABMTokensEndpoint, nil)
ue.GET("/api/_version_/fleet/abm_tokens/count", countABMTokensEndpoint, nil)
ue.PATCH("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/teams", updateABMTokenTeamsEndpoint, updateABMTokenTeamsRequest{})
ue.PATCH("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/renew", renewABMTokenEndpoint, renewABMTokenRequest{})

Expand Down
7 changes: 7 additions & 0 deletions server/service/integration_mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,10 @@ func (s *integrationMDMTestSuite) TestAppleGetAppleMDM() {
require.Equal(t, "Fleet", mdmResp.CommonName)
require.NotZero(t, mdmResp.RenewDate)

var countTokensResp countABMTokensResponse
s.DoJSON("GET", "/api/latest/fleet/abm_tokens/count", nil, http.StatusOK, &countTokensResp)
assert.EqualValues(t, 0, countTokensResp.Count)

// set up multiple ABM tokens with different org names
defaultOrgName := "fleet_test"
s.enableABM(defaultOrgName)
Expand Down Expand Up @@ -860,6 +864,9 @@ func (s *integrationMDMTestSuite) TestAppleGetAppleMDM() {
require.Equal(t, fleet.TeamNameNoTeam, tok.IOSTeam.Name)
require.Equal(t, fleet.TeamNameNoTeam, tok.IPadOSTeam.Name)

s.DoJSON("GET", "/api/latest/fleet/abm_tokens/count", nil, http.StatusOK, &countTokensResp)
assert.EqualValues(t, 2, countTokensResp.Count)

// create a new team
tm, err := s.ds.NewTeam(context.Background(), &fleet.Team{
Name: t.Name(),
Expand Down

0 comments on commit 15a8214

Please sign in to comment.