Skip to content

Commit

Permalink
Added GET enterprise API endpoint. (#26555)
Browse files Browse the repository at this point in the history
For #26218 

- Added `GET /api/_version_/fleet/android_enterprise` andpoint and tests
- Set up some testing infrastructure for Android service tests -- see
new README.md

# Checklist for submitter

- [x] Added/updated automated tests
- [x] Manual QA for all new/changed functionality
  • Loading branch information
getvictor authored Feb 26, 2025
1 parent d903cf9 commit 3d5666d
Show file tree
Hide file tree
Showing 29 changed files with 854 additions and 112 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ generate-dev: .prefix
NODE_ENV=development yarn run webpack --progress --watch

generate-mock: .prefix
go generate github.com/fleetdm/fleet/v4/server/mock github.com/fleetdm/fleet/v4/server/mock/mockresult github.com/fleetdm/fleet/v4/server/service/mock
go generate github.com/fleetdm/fleet/v4/server/mock github.com/fleetdm/fleet/v4/server/mock/mockresult github.com/fleetdm/fleet/v4/server/service/mock github.com/fleetdm/fleet/v4/server/mdm/android/mock

generate-doc: .prefix
go generate github.com/fleetdm/fleet/v4/server/fleet
Expand Down
2 changes: 1 addition & 1 deletion server/authz/policy.rego
Original file line number Diff line number Diff line change
Expand Up @@ -1027,5 +1027,5 @@ allow {
allow {
object.type == "android_enterprise"
subject.global_role == admin
action == write
action == [read, write][_]
}
1 change: 1 addition & 0 deletions server/mdm/android/arch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func TestAllAndroidPackageDependencies(t *testing.T) {
"github.com/fleetdm/fleet/v4/server/service/middleware/auth",
"github.com/fleetdm/fleet/v4/server/service/middleware/authzcheck",
"github.com/fleetdm/fleet/v4/server/service/middleware/endpoint_utils",
"github.com/fleetdm/fleet/v4/server/service/middleware/log",
"github.com/fleetdm/fleet/v4/server/service/middleware/ratelimit",
).
ShouldNotDependOn(
Expand Down
2 changes: 1 addition & 1 deletion server/mdm/android/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Datastore interface {
GetEnterpriseByID(ctx context.Context, ID uint) (*EnterpriseDetails, error)
GetEnterprise(ctx context.Context) (*Enterprise, error)
UpdateEnterprise(ctx context.Context, enterprise *EnterpriseDetails) error
DeleteEnterprises(ctx context.Context) error
DeleteAllEnterprises(ctx context.Context) error
DeleteOtherEnterprises(ctx context.Context, ID uint) error

CreateDeviceTx(ctx context.Context, tx sqlx.ExtContext, device *Device) (*Device, error)
Expand Down
4 changes: 4 additions & 0 deletions server/mdm/android/mock/android.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package mock

//go:generate go run ../../../mock/mockimpl/impl.go -o proxy.go "p *Proxy" "android.Proxy"
//go:generate go run ../../../mock/mockimpl/impl.go -o datastore.go "ds *Datastore" "android.Datastore"
113 changes: 113 additions & 0 deletions server/mdm/android/mock/datastore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Automatically generated by mockimpl. DO NOT EDIT!

package mock

import (
"context"
"sync"

"github.com/fleetdm/fleet/v4/server/mdm/android"
"github.com/jmoiron/sqlx"
)

var _ android.Datastore = (*Datastore)(nil)

type CreateEnterpriseFunc func(ctx context.Context) (uint, error)

type GetEnterpriseByIDFunc func(ctx context.Context, ID uint) (*android.EnterpriseDetails, error)

type GetEnterpriseFunc func(ctx context.Context) (*android.Enterprise, error)

type UpdateEnterpriseFunc func(ctx context.Context, enterprise *android.EnterpriseDetails) error

type DeleteAllEnterprisesFunc func(ctx context.Context) error

type DeleteOtherEnterprisesFunc func(ctx context.Context, ID uint) error

type CreateDeviceTxFunc func(ctx context.Context, tx sqlx.ExtContext, device *android.Device) (*android.Device, error)

type UpdateDeviceTxFunc func(ctx context.Context, tx sqlx.ExtContext, device *android.Device) error

type Datastore struct {
CreateEnterpriseFunc CreateEnterpriseFunc
CreateEnterpriseFuncInvoked bool

GetEnterpriseByIDFunc GetEnterpriseByIDFunc
GetEnterpriseByIDFuncInvoked bool

GetEnterpriseFunc GetEnterpriseFunc
GetEnterpriseFuncInvoked bool

UpdateEnterpriseFunc UpdateEnterpriseFunc
UpdateEnterpriseFuncInvoked bool

DeleteAllEnterprisesFunc DeleteAllEnterprisesFunc
DeleteAllEnterprisesFuncInvoked bool

DeleteOtherEnterprisesFunc DeleteOtherEnterprisesFunc
DeleteOtherEnterprisesFuncInvoked bool

CreateDeviceTxFunc CreateDeviceTxFunc
CreateDeviceTxFuncInvoked bool

UpdateDeviceTxFunc UpdateDeviceTxFunc
UpdateDeviceTxFuncInvoked bool

mu sync.Mutex
}

func (ds *Datastore) CreateEnterprise(ctx context.Context) (uint, error) {
ds.mu.Lock()
ds.CreateEnterpriseFuncInvoked = true
ds.mu.Unlock()
return ds.CreateEnterpriseFunc(ctx)
}

func (ds *Datastore) GetEnterpriseByID(ctx context.Context, ID uint) (*android.EnterpriseDetails, error) {
ds.mu.Lock()
ds.GetEnterpriseByIDFuncInvoked = true
ds.mu.Unlock()
return ds.GetEnterpriseByIDFunc(ctx, ID)
}

func (ds *Datastore) GetEnterprise(ctx context.Context) (*android.Enterprise, error) {
ds.mu.Lock()
ds.GetEnterpriseFuncInvoked = true
ds.mu.Unlock()
return ds.GetEnterpriseFunc(ctx)
}

func (ds *Datastore) UpdateEnterprise(ctx context.Context, enterprise *android.EnterpriseDetails) error {
ds.mu.Lock()
ds.UpdateEnterpriseFuncInvoked = true
ds.mu.Unlock()
return ds.UpdateEnterpriseFunc(ctx, enterprise)
}

func (ds *Datastore) DeleteAllEnterprises(ctx context.Context) error {
ds.mu.Lock()
ds.DeleteAllEnterprisesFuncInvoked = true
ds.mu.Unlock()
return ds.DeleteAllEnterprisesFunc(ctx)
}

func (ds *Datastore) DeleteOtherEnterprises(ctx context.Context, ID uint) error {
ds.mu.Lock()
ds.DeleteOtherEnterprisesFuncInvoked = true
ds.mu.Unlock()
return ds.DeleteOtherEnterprisesFunc(ctx, ID)
}

func (ds *Datastore) CreateDeviceTx(ctx context.Context, tx sqlx.ExtContext, device *android.Device) (*android.Device, error) {
ds.mu.Lock()
ds.CreateDeviceTxFuncInvoked = true
ds.mu.Unlock()
return ds.CreateDeviceTxFunc(ctx, tx, device)
}

func (ds *Datastore) UpdateDeviceTx(ctx context.Context, tx sqlx.ExtContext, device *android.Device) error {
ds.mu.Lock()
ds.UpdateDeviceTxFuncInvoked = true
ds.mu.Unlock()
return ds.UpdateDeviceTxFunc(ctx, tx, device)
}
28 changes: 28 additions & 0 deletions server/mdm/android/mock/datastore_setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package mock

import (
"context"

"github.com/fleetdm/fleet/v4/server/mdm/android"
)

func (s *Datastore) InitCommonMocks() {
s.CreateEnterpriseFunc = func(ctx context.Context) (uint, error) {
return 1, nil
}
s.UpdateEnterpriseFunc = func(ctx context.Context, enterprise *android.EnterpriseDetails) error {
return nil
}
s.GetEnterpriseFunc = func(ctx context.Context) (*android.Enterprise, error) {
return &android.Enterprise{}, nil
}
s.GetEnterpriseByIDFunc = func(ctx context.Context, ID uint) (*android.EnterpriseDetails, error) {
return &android.EnterpriseDetails{}, nil
}
s.DeleteAllEnterprisesFunc = func(ctx context.Context) error {
return nil
}
s.DeleteOtherEnterprisesFunc = func(ctx context.Context, ID uint) error {
return nil
}
}
77 changes: 77 additions & 0 deletions server/mdm/android/mock/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Automatically generated by mockimpl. DO NOT EDIT!

package mock

import (
"context"
"sync"

"github.com/fleetdm/fleet/v4/server/mdm/android"
"google.golang.org/api/androidmanagement/v1"
)

var _ android.Proxy = (*Proxy)(nil)

type SignupURLsCreateFunc func(callbackURL string) (*android.SignupDetails, error)

type EnterprisesCreateFunc func(ctx context.Context, req android.ProxyEnterprisesCreateRequest) (string, string, error)

type EnterprisesPoliciesPatchFunc func(enterpriseID string, policyName string, policy *androidmanagement.Policy) error

type EnterprisesEnrollmentTokensCreateFunc func(enterpriseName string, token *androidmanagement.EnrollmentToken) (*androidmanagement.EnrollmentToken, error)

type EnterpriseDeleteFunc func(enterpriseID string) error

type Proxy struct {
SignupURLsCreateFunc SignupURLsCreateFunc
SignupURLsCreateFuncInvoked bool

EnterprisesCreateFunc EnterprisesCreateFunc
EnterprisesCreateFuncInvoked bool

EnterprisesPoliciesPatchFunc EnterprisesPoliciesPatchFunc
EnterprisesPoliciesPatchFuncInvoked bool

EnterprisesEnrollmentTokensCreateFunc EnterprisesEnrollmentTokensCreateFunc
EnterprisesEnrollmentTokensCreateFuncInvoked bool

EnterpriseDeleteFunc EnterpriseDeleteFunc
EnterpriseDeleteFuncInvoked bool

mu sync.Mutex
}

func (p *Proxy) SignupURLsCreate(callbackURL string) (*android.SignupDetails, error) {
p.mu.Lock()
p.SignupURLsCreateFuncInvoked = true
p.mu.Unlock()
return p.SignupURLsCreateFunc(callbackURL)
}

func (p *Proxy) EnterprisesCreate(ctx context.Context, req android.ProxyEnterprisesCreateRequest) (string, string, error) {
p.mu.Lock()
p.EnterprisesCreateFuncInvoked = true
p.mu.Unlock()
return p.EnterprisesCreateFunc(ctx, req)
}

func (p *Proxy) EnterprisesPoliciesPatch(enterpriseID string, policyName string, policy *androidmanagement.Policy) error {
p.mu.Lock()
p.EnterprisesPoliciesPatchFuncInvoked = true
p.mu.Unlock()
return p.EnterprisesPoliciesPatchFunc(enterpriseID, policyName, policy)
}

func (p *Proxy) EnterprisesEnrollmentTokensCreate(enterpriseName string, token *androidmanagement.EnrollmentToken) (*androidmanagement.EnrollmentToken, error) {
p.mu.Lock()
p.EnterprisesEnrollmentTokensCreateFuncInvoked = true
p.mu.Unlock()
return p.EnterprisesEnrollmentTokensCreateFunc(enterpriseName, token)
}

func (p *Proxy) EnterpriseDelete(enterpriseID string) error {
p.mu.Lock()
p.EnterpriseDeleteFuncInvoked = true
p.mu.Unlock()
return p.EnterpriseDeleteFunc(enterpriseID)
}
23 changes: 23 additions & 0 deletions server/mdm/android/mock/proxy_setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package mock

import (
"context"

"github.com/fleetdm/fleet/v4/server/mdm/android"
"google.golang.org/api/androidmanagement/v1"
)

func (p *Proxy) InitCommonMocks() {
p.EnterpriseDeleteFunc = func(enterpriseID string) error {
return nil
}
p.SignupURLsCreateFunc = func(callbackURL string) (*android.SignupDetails, error) {
return &android.SignupDetails{}, nil
}
p.EnterprisesCreateFunc = func(ctx context.Context, req android.ProxyEnterprisesCreateRequest) (string, string, error) {
return "enterpriseName", "projects/project/topics/topic", nil
}
p.EnterprisesPoliciesPatchFunc = func(enterpriseID string, policyName string, policy *androidmanagement.Policy) error {
return nil
}
}
2 changes: 1 addition & 1 deletion server/mdm/android/mysql/enterprises.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (ds *Datastore) DeleteOtherEnterprises(ctx context.Context, id uint) error
return nil
}

func (ds *Datastore) DeleteEnterprises(ctx context.Context) error {
func (ds *Datastore) DeleteAllEnterprises(ctx context.Context) error {
stmt := `DELETE FROM android_enterprises`
_, err := ds.Writer(ctx).ExecContext(ctx, stmt)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions server/mdm/android/mysql/enterprises_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestEnterprise(t *testing.T) {
}{
{"CreateGetEnterprise", testCreateGetEnterprise},
{"UpdateEnterprise", testUpdateEnterprise},
{"DeleteEnterprises", testDeleteEnterprises},
{"DeleteAllEnterprises", testDeleteEnterprises},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
Expand Down Expand Up @@ -75,7 +75,7 @@ func testUpdateEnterprise(t *testing.T, ds *Datastore) {
}

func testDeleteEnterprises(t *testing.T, ds *Datastore) {
err := ds.DeleteEnterprises(testCtx())
err := ds.DeleteAllEnterprises(testCtx())
require.NoError(t, err)
err = ds.DeleteOtherEnterprises(testCtx(), 9999)
require.NoError(t, err)
Expand Down Expand Up @@ -108,7 +108,7 @@ func testDeleteEnterprises(t *testing.T, ds *Datastore) {
_, err = ds.GetEnterpriseByID(testCtx(), tempEnterprise.ID)
assert.True(t, fleet.IsNotFound(err))

err = ds.DeleteEnterprises(testCtx())
err = ds.DeleteAllEnterprises(testCtx())
require.NoError(t, err)
_, err = ds.GetEnterpriseByID(testCtx(), enterprise.ID)
assert.True(t, fleet.IsNotFound(err))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,26 @@ import (
"github.com/stretchr/testify/require"
)

// Android MySQL testing utilities.
// Android MySQL testing utilities. This file should contain VERY LITTLE code since it is also compiled into the production binary.
// Whenever possible, new code should go into a dedicated testing package (e.g. mdm/android/mysql/tests/testing_utils.go).
// These utilities are used to create a MySQL Datastore for testing the Android MDM MySQL implementation.
// They are located in the same package as the implementation to prevent a circular dependency.
// They are located in the same package as the implementation to prevent a circular dependency. If put it in a different package,
// the circular dependency would be: mysql -> testing_utils -> mysql

func CreateMySQLDS(t testing.TB) *Datastore {
return createMySQLDSWithOptions(t, nil)
}

func createMySQLDSWithOptions(t testing.TB, opts *testing_utils.DatastoreTestOptions) *Datastore {
cleanTestName, opts := testing_utils.ProcessOptions(t, opts)
ds := initializeDatabase(t, cleanTestName, opts)
ds := InitializeDatabase(t, cleanTestName, opts)
t.Cleanup(func() { Close(ds) })
return ds
}

// initializeDatabase loads the dumped schema into a newly created database in MySQL.
// InitializeDatabase loads the dumped schema into a newly created database in MySQL.
// This is much faster than running the full set of migrations on each test.
func initializeDatabase(t testing.TB, testName string, opts *testing_utils.DatastoreTestOptions) *Datastore {
func InitializeDatabase(t testing.TB, testName string, opts *testing_utils.DatastoreTestOptions) *Datastore {
_, filename, _, _ := runtime.Caller(0)
schemaPath := path.Join(path.Dir(filename), "schema.sql")
testing_utils.LoadSchema(t, testName, opts, schemaPath)
Expand Down
22 changes: 22 additions & 0 deletions server/mdm/android/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package android

import (
"context"

"google.golang.org/api/androidmanagement/v1"
)

type Proxy interface {
SignupURLsCreate(callbackURL string) (*SignupDetails, error)
EnterprisesCreate(ctx context.Context, req ProxyEnterprisesCreateRequest) (string, string, error)
EnterprisesPoliciesPatch(enterpriseID string, policyName string, policy *androidmanagement.Policy) error
EnterprisesEnrollmentTokensCreate(enterpriseName string, token *androidmanagement.EnrollmentToken) (*androidmanagement.EnrollmentToken, error)
EnterpriseDelete(enterpriseID string) error
}

type ProxyEnterprisesCreateRequest struct {
androidmanagement.Enterprise
EnterpriseToken string
SignupUrlName string
PubSubPushURL string
}
Loading

0 comments on commit 3d5666d

Please sign in to comment.