Update queries APIs that drive the OS settings UI (#36018)
**Related issue:** Resolves #35532 Update queries APIs that drive the OS settings UI
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* updated queries / APIs that drive the OS Settings UI to include the status of host cert templates.
|
||||
@@ -2,6 +2,7 @@ package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -205,3 +206,82 @@ func (ds *Datastore) UpdateCertificateStatus(ctx context.Context, hostUUID strin
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetHostCertificateTemplates(ctx context.Context, hostUUID string) ([]fleet.HostCertificateTemplate, error) {
|
||||
if hostUUID == "" {
|
||||
return nil, errors.New("hostUUID cannot be empty")
|
||||
}
|
||||
|
||||
stmt := `
|
||||
SELECT
|
||||
ct.name,
|
||||
hct.status
|
||||
FROM host_certificate_templates hct
|
||||
INNER JOIN certificate_templates ct ON ct.id = hct.certificate_template_id
|
||||
WHERE hct.host_uuid = ?`
|
||||
|
||||
var hTemplates []fleet.HostCertificateTemplate
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &hTemplates, stmt, hostUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hTemplates, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDMProfileSummaryFromHostCertificateTemplates(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
|
||||
var stmt string
|
||||
var args []interface{}
|
||||
|
||||
if teamID != nil && *teamID > 0 {
|
||||
stmt = `
|
||||
SELECT
|
||||
hct.status AS status,
|
||||
COUNT(DISTINCT hct.host_uuid) AS n
|
||||
FROM host_certificate_templates hct
|
||||
INNER JOIN certificate_templates ct ON hct.certificate_template_id = ct.id
|
||||
WHERE ct.team_id = ?
|
||||
GROUP BY 1`
|
||||
args = append(args, *teamID)
|
||||
} else {
|
||||
stmt = `
|
||||
SELECT
|
||||
hct.status AS status,
|
||||
COUNT(DISTINCT hct.host_uuid) AS n
|
||||
FROM host_certificate_templates hct
|
||||
GROUP BY 1`
|
||||
}
|
||||
|
||||
var dest []struct {
|
||||
Count uint `db:"n"`
|
||||
Status string `db:"status"`
|
||||
}
|
||||
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &dest, stmt, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
byStatus := make(map[string]uint)
|
||||
for _, s := range dest {
|
||||
if _, ok := byStatus[s.Status]; ok {
|
||||
return nil, fmt.Errorf("duplicate status %s found", s.Status)
|
||||
}
|
||||
byStatus[s.Status] = s.Count
|
||||
}
|
||||
|
||||
var res fleet.MDMProfilesSummary
|
||||
for s, c := range byStatus {
|
||||
switch fleet.MDMDeliveryStatus(s) {
|
||||
case fleet.MDMDeliveryFailed:
|
||||
res.Failed = c
|
||||
case fleet.MDMDeliveryPending:
|
||||
res.Pending = c
|
||||
case fleet.MDMDeliveryVerifying:
|
||||
res.Verifying = c
|
||||
case fleet.MDMDeliveryVerified:
|
||||
res.Verified = c
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown status %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ package mysql
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/test"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -25,6 +28,8 @@ func TestCertificates(t *testing.T) {
|
||||
{"DeleteCertificateTemplate", testDeleteCertificateTemplate},
|
||||
{"BatchUpsertCertificates", testBatchUpsertCertificates},
|
||||
{"BatchDeleteCertificateTemplates", testBatchDeleteCertificateTemplates},
|
||||
{"GetHostCertificateTemplates", testGetHostCertificateTemplates},
|
||||
{"GetMDMProfileSummaryFromHostCertificateTemplates", testGetMDMProfileSummaryFromHostCertificateTemplates},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -780,3 +785,218 @@ WHERE host_uuid = ? AND certificate_template_id = ?;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testGetHostCertificateTemplates(t *testing.T, ds *Datastore) {
|
||||
defer TruncateTables(t, ds)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
h1 := test.NewHost(t, ds, "host_1", "127.0.0.1", "1", "1", time.Now())
|
||||
h2 := test.NewHost(t, ds, "host_2", "127.0.0.2", "2", "2", time.Now())
|
||||
|
||||
team, err := ds.NewTeam(ctx, &fleet.Team{Name: "Test Team"})
|
||||
require.NoError(t, err)
|
||||
|
||||
h2.TeamID = &team.ID
|
||||
err = ds.UpdateHost(ctx, h2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a test certificate authority
|
||||
ca, err := ds.NewCertificateAuthority(ctx, &fleet.CertificateAuthority{
|
||||
Type: string(fleet.CATypeCustomSCEPProxy),
|
||||
Name: ptr.String("Test SCEP CA"),
|
||||
URL: ptr.String("http://localhost:8080/scep"),
|
||||
Challenge: ptr.String("test-challenge"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create some certificate templates
|
||||
ct1, err := ds.CreateCertificateTemplate(ctx, &fleet.CertificateTemplate{
|
||||
Name: "AAA",
|
||||
TeamID: team.ID,
|
||||
CertificateAuthorityID: ca.ID,
|
||||
SubjectName: "CN=Test Subject 1",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ct2, err := ds.CreateCertificateTemplate(ctx, &fleet.CertificateTemplate{
|
||||
Name: "BBB",
|
||||
TeamID: team.ID,
|
||||
CertificateAuthorityID: ca.ID,
|
||||
SubjectName: "CN=Test Subject 2",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set the installation status on the certificate templates
|
||||
// TODO: Refactor this to use UpdateStatus DB method when available
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
"INSERT INTO host_certificate_templates (host_uuid, certificate_template_id, fleet_challenge, status) VALUES (?, ?, ?, ?)",
|
||||
h2.UUID, ct1.ID, "test-challenge", fleet.OSSettingsVerified,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
"INSERT INTO host_certificate_templates (host_uuid, certificate_template_id, fleet_challenge, status) VALUES (?, ?, ?, ?)",
|
||||
h2.UUID, ct2.ID, "test-challenge", fleet.OSSettingsFailed,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
do func(*testing.T, *Datastore)
|
||||
}{
|
||||
{
|
||||
"hostUUID is not provided",
|
||||
func(t *testing.T, ds *Datastore) {
|
||||
_, err := ds.GetHostCertificateTemplates(ctx, "")
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
"No certificate templates found",
|
||||
func(t *testing.T, ds *Datastore) {
|
||||
templates, err := ds.GetHostCertificateTemplates(ctx, h1.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, templates)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Returns the certificates available for the host",
|
||||
func(t *testing.T, datastore *Datastore) {
|
||||
templates, err := ds.GetHostCertificateTemplates(ctx, h2.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, templates, 2)
|
||||
|
||||
// Sort the templates by name to make results deterministic
|
||||
sort.Slice(templates, func(i, j int) bool { return templates[i].Name < templates[j].Name })
|
||||
|
||||
require.Equal(t, ct1.Name, templates[0].Name)
|
||||
require.Equal(t, fleet.MDMDeliveryVerified, templates[0].Status)
|
||||
|
||||
require.Equal(t, ct2.Name, templates[1].Name)
|
||||
require.Equal(t, fleet.MDMDeliveryFailed, templates[1].Status)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.do(t, ds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testGetMDMProfileSummaryFromHostCertificateTemplates(t *testing.T, ds *Datastore) {
|
||||
defer TruncateTables(t, ds)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Doing this with no data in the table should be ok
|
||||
result, err := ds.GetMDMProfileSummaryFromHostCertificateTemplates(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint(0), result.Pending)
|
||||
require.Equal(t, uint(0), result.Failed)
|
||||
require.Equal(t, uint(0), result.Verified)
|
||||
require.Equal(t, uint(0), result.Verifying)
|
||||
|
||||
h1 := test.NewHost(t, ds, "host_1", "127.0.0.1", "1", "1", time.Now())
|
||||
h2 := test.NewHost(t, ds, "host_2", "127.0.0.2", "2", "2", time.Now())
|
||||
|
||||
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "Team 1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "Team 2"})
|
||||
require.NoError(t, err)
|
||||
|
||||
h1.TeamID = &team1.ID
|
||||
err = ds.UpdateHost(ctx, h2)
|
||||
require.NoError(t, err)
|
||||
|
||||
h2.TeamID = &team2.ID
|
||||
err = ds.UpdateHost(ctx, h2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a test certificate authority
|
||||
ca, err := ds.NewCertificateAuthority(ctx, &fleet.CertificateAuthority{
|
||||
Type: string(fleet.CATypeCustomSCEPProxy),
|
||||
Name: ptr.String("Test SCEP CA"),
|
||||
URL: ptr.String("http://localhost:8080/scep"),
|
||||
Challenge: ptr.String("test-challenge"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create some certificate templates
|
||||
ct1, err := ds.CreateCertificateTemplate(ctx, &fleet.CertificateTemplate{
|
||||
Name: "AAA",
|
||||
TeamID: team1.ID,
|
||||
CertificateAuthorityID: ca.ID,
|
||||
SubjectName: "CN=Test Subject 1",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ct2, err := ds.CreateCertificateTemplate(ctx, &fleet.CertificateTemplate{
|
||||
Name: "BBB",
|
||||
TeamID: team2.ID,
|
||||
CertificateAuthorityID: ca.ID,
|
||||
SubjectName: "CN=Test Subject 2",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set the installation status on the certificate templates
|
||||
// TODO: Refactor this to use UpdateStatus DB method when available
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
"INSERT INTO host_certificate_templates (host_uuid, certificate_template_id, fleet_challenge, status) VALUES (?, ?, ?, ?)",
|
||||
h1.UUID, ct1.ID, "test-challenge", fleet.OSSettingsPending,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
"INSERT INTO host_certificate_templates (host_uuid, certificate_template_id, fleet_challenge, status) VALUES (?, ?, ?, ?)",
|
||||
h2.UUID, ct1.ID, "test-challenge", fleet.OSSettingsVerified,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ds.writer(ctx).ExecContext(ctx,
|
||||
"INSERT INTO host_certificate_templates (host_uuid, certificate_template_id, fleet_challenge, status) VALUES (?, ?, ?, ?)",
|
||||
h2.UUID, ct2.ID, "test-challenge", fleet.OSSettingsFailed,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
do func(*testing.T, *Datastore)
|
||||
}{
|
||||
{
|
||||
"no teamID provided",
|
||||
func(t *testing.T, ds *Datastore) {
|
||||
result, err := ds.GetMDMProfileSummaryFromHostCertificateTemplates(ctx, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, uint(1), result.Pending)
|
||||
require.Equal(t, uint(1), result.Verified)
|
||||
require.Equal(t, uint(1), result.Failed)
|
||||
},
|
||||
},
|
||||
{
|
||||
"teamID provided",
|
||||
func(t *testing.T, ds *Datastore) {
|
||||
result, err := ds.GetMDMProfileSummaryFromHostCertificateTemplates(ctx, &team1.ID)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
require.Equal(t, uint(0), result.Failed)
|
||||
require.Equal(t, uint(1), result.Verified)
|
||||
require.Equal(t, uint(1), result.Pending)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.do(t, ds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,3 +31,24 @@ type CertificateTemplateResponseFull struct {
|
||||
SubjectName string `json:"subject_name" db:"subject_name"`
|
||||
TeamID uint `json:"-" db:"team_id"`
|
||||
}
|
||||
|
||||
// HostCertificateTemplate represents a certificate template associated with a particular host
|
||||
type HostCertificateTemplate struct {
|
||||
HostUUID string `db:"host_uuid" json:"-"`
|
||||
Name string `db:"name" json:"-"`
|
||||
Status MDMDeliveryStatus `db:"status" json:"-"`
|
||||
}
|
||||
|
||||
// ToHostMDMProfile maps a HostCertificateTemplate to a HostMDMProfile, suitable for use in the MDM API
|
||||
func (p *HostCertificateTemplate) ToHostMDMProfile() HostMDMProfile {
|
||||
if p == nil {
|
||||
return HostMDMProfile{}
|
||||
}
|
||||
|
||||
return HostMDMProfile{
|
||||
HostUUID: p.HostUUID,
|
||||
Name: p.Name,
|
||||
Platform: "android",
|
||||
Status: &p.Status,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHostCertificateTemplate(t *testing.T) {
|
||||
t.Run("ToHostMDMProfile", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
template *HostCertificateTemplate
|
||||
expectation func(*testing.T, HostMDMProfile)
|
||||
}{
|
||||
{
|
||||
name: "nil template",
|
||||
template: nil,
|
||||
expectation: func(t *testing.T, profile HostMDMProfile) {
|
||||
require.Equal(t, "", profile.HostUUID)
|
||||
require.Equal(t, "", profile.Name)
|
||||
require.Equal(t, "", profile.Platform)
|
||||
require.Nil(t, profile.Status)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "maps fields correctly",
|
||||
template: &HostCertificateTemplate{
|
||||
HostUUID: "1234",
|
||||
Name: "HostCertificate",
|
||||
Status: MDMDeliveryVerified,
|
||||
},
|
||||
expectation: func(t *testing.T, profile HostMDMProfile) {
|
||||
require.Equal(t, "1234", profile.HostUUID)
|
||||
require.Equal(t, "HostCertificate", profile.Name)
|
||||
require.Equal(t, "android", profile.Platform)
|
||||
require.Equal(t, MDMDeliveryVerified, *profile.Status)
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.expectation(t, tt.template.ToHostMDMProfile())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -2338,6 +2338,13 @@ type Datastore interface {
|
||||
// assigned to any team).
|
||||
GetMDMAndroidProfilesSummary(ctx context.Context, teamID *uint) (*MDMProfilesSummary, error)
|
||||
|
||||
// GetCertificateStatusSummary GetCertificateTemplatesSummary returns a summary of the current state of certificate templates on each host in
|
||||
// the specified team (or, if no team is specified, each host that is not assigned to any team).
|
||||
GetMDMProfileSummaryFromHostCertificateTemplates(ctx context.Context, teamID *uint) (*MDMProfilesSummary, error)
|
||||
|
||||
// GetHostCertificateTemplates returns what certificate templates are currently associated with the specified host.
|
||||
GetHostCertificateTemplates(ctx context.Context, hostUUID string) ([]HostCertificateTemplate, error)
|
||||
|
||||
// GetHostMDMAndroidProfiles retrieves the Android MDM profiles for a specific host.
|
||||
GetHostMDMAndroidProfiles(ctx context.Context, hostUUID string) ([]HostMDMAndroidProfile, error)
|
||||
|
||||
|
||||
+21
-4
@@ -405,14 +405,14 @@ type MDMDiskEncryptionSummary struct {
|
||||
}
|
||||
|
||||
// MDMProfilesSummary reports the number of hosts being managed with configuration
|
||||
// profiles and/or disk encryption. Each host may be counted in only one of four mutually-exclusive categories:
|
||||
// Failed, Pending, Verifying, or Verified.
|
||||
// profiles, disk encryption or certificate templates.
|
||||
// Each host may be counted in only one of four mutually exclusive categories: Failed, Pending, Verifying, or Verified.
|
||||
type MDMProfilesSummary struct {
|
||||
// Verified includes each host where Fleet has verified the installation of all of the
|
||||
// Verified includes each host where Fleet has verified the installation of all the
|
||||
// profiles currently applicable to the host. If any of the profiles are pending, failed, or
|
||||
// subject to verification for the host, the host is not counted as verified.
|
||||
Verified uint `json:"verified" db:"verified"`
|
||||
// Verifying includes each host where the MDM service has successfully delivered all of the
|
||||
// Verifying includes each host where the MDM service has successfully delivered all the
|
||||
// profiles currently applicable to the host. If any of the profiles are pending or failed for
|
||||
// the host, the host is not counted as verifying.
|
||||
Verifying uint `json:"verifying" db:"verifying"`
|
||||
@@ -424,6 +424,23 @@ type MDMProfilesSummary struct {
|
||||
Failed uint `json:"failed" db:"failed"`
|
||||
}
|
||||
|
||||
func (mdmPS *MDMProfilesSummary) Add(other *MDMProfilesSummary) *MDMProfilesSummary {
|
||||
var s1, s2 MDMProfilesSummary
|
||||
if mdmPS != nil {
|
||||
s1 = *mdmPS
|
||||
}
|
||||
if other != nil {
|
||||
s2 = *other
|
||||
}
|
||||
return &MDMProfilesSummary{
|
||||
Verified: s1.Verified + s2.Verified,
|
||||
Verifying: s1.Verifying + s2.Verifying,
|
||||
Pending: s1.Pending + s2.Pending,
|
||||
Failed: s1.Failed + s2.Failed,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// HostMDMProfile is the status of an MDM profile on a host. It can be used to represent either
|
||||
// a Windows or macOS profile.
|
||||
type HostMDMProfile struct {
|
||||
|
||||
@@ -717,3 +717,68 @@ func TestFilterOutUserScopedProfiles(t *testing.T) {
|
||||
|
||||
require.ElementsMatch(t, filteredProfiles, []*fleet.MDMAppleProfilePayload{&systemScopedProfile})
|
||||
}
|
||||
|
||||
func TestMDMProfilesSummary(t *testing.T) {
|
||||
t.Run("Add", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a *fleet.MDMProfilesSummary
|
||||
b *fleet.MDMProfilesSummary
|
||||
expected *fleet.MDMProfilesSummary
|
||||
}{
|
||||
{
|
||||
name: "Both nil",
|
||||
a: nil,
|
||||
b: nil,
|
||||
expected: &fleet.MDMProfilesSummary{Verified: 0, Verifying: 0, Pending: 0, Failed: 0},
|
||||
},
|
||||
{
|
||||
name: "First nil",
|
||||
a: nil,
|
||||
b: &fleet.MDMProfilesSummary{Verified: 1, Verifying: 2, Pending: 3, Failed: 4},
|
||||
expected: &fleet.MDMProfilesSummary{Verified: 1, Verifying: 2, Pending: 3, Failed: 4},
|
||||
},
|
||||
{
|
||||
name: "Second nil",
|
||||
a: &fleet.MDMProfilesSummary{Verified: 1, Verifying: 2, Pending: 3, Failed: 4},
|
||||
b: nil,
|
||||
expected: &fleet.MDMProfilesSummary{Verified: 1, Verifying: 2, Pending: 3, Failed: 4},
|
||||
},
|
||||
{
|
||||
name: "Combined values",
|
||||
a: &fleet.MDMProfilesSummary{Verified: 2, Verifying: 4, Pending: 6, Failed: 8},
|
||||
b: &fleet.MDMProfilesSummary{Verified: 1, Verifying: 3, Pending: 5, Failed: 7},
|
||||
expected: &fleet.MDMProfilesSummary{
|
||||
Verified: 3,
|
||||
Verifying: 7,
|
||||
Pending: 11,
|
||||
Failed: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All zero values",
|
||||
a: &fleet.MDMProfilesSummary{Verified: 0, Verifying: 0, Pending: 0, Failed: 0},
|
||||
b: &fleet.MDMProfilesSummary{Verified: 0, Verifying: 0, Pending: 0, Failed: 0},
|
||||
expected: &fleet.MDMProfilesSummary{Verified: 0, Verifying: 0, Pending: 0, Failed: 0},
|
||||
},
|
||||
{
|
||||
name: "Large values",
|
||||
a: &fleet.MDMProfilesSummary{Verified: 1_000_000, Verifying: 2_000_000, Pending: 3_000_000, Failed: 4_000_000},
|
||||
b: &fleet.MDMProfilesSummary{Verified: 5_000_000, Verifying: 6_000_000, Pending: 7_000_000, Failed: 8_000_000},
|
||||
expected: &fleet.MDMProfilesSummary{
|
||||
Verified: 6_000_000,
|
||||
Verifying: 8_000_000,
|
||||
Pending: 10_000_000,
|
||||
Failed: 12_000_000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.a.Add(tt.b)
|
||||
require.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1515,6 +1515,10 @@ type DeleteMDMAndroidConfigProfileFunc func(ctx context.Context, profileUUID str
|
||||
|
||||
type GetMDMAndroidProfilesSummaryFunc func(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error)
|
||||
|
||||
type GetMDMProfileSummaryFromHostCertificateTemplatesFunc func(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error)
|
||||
|
||||
type GetHostCertificateTemplatesFunc func(ctx context.Context, hostUUID string) ([]fleet.HostCertificateTemplate, error)
|
||||
|
||||
type GetHostMDMAndroidProfilesFunc func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAndroidProfile, error)
|
||||
|
||||
type NewAndroidPolicyRequestFunc func(ctx context.Context, req *fleet.MDMAndroidPolicyRequest) error
|
||||
@@ -3874,6 +3878,12 @@ type DataStore struct {
|
||||
GetMDMAndroidProfilesSummaryFunc GetMDMAndroidProfilesSummaryFunc
|
||||
GetMDMAndroidProfilesSummaryFuncInvoked bool
|
||||
|
||||
GetMDMProfileSummaryFromHostCertificateTemplatesFunc GetMDMProfileSummaryFromHostCertificateTemplatesFunc
|
||||
GetMDMProfileSummaryFromHostCertificateTemplatesFuncInvoked bool
|
||||
|
||||
GetHostCertificateTemplatesFunc GetHostCertificateTemplatesFunc
|
||||
GetHostCertificateTemplatesFuncInvoked bool
|
||||
|
||||
GetHostMDMAndroidProfilesFunc GetHostMDMAndroidProfilesFunc
|
||||
GetHostMDMAndroidProfilesFuncInvoked bool
|
||||
|
||||
@@ -9279,6 +9289,20 @@ func (s *DataStore) GetMDMAndroidProfilesSummary(ctx context.Context, teamID *ui
|
||||
return s.GetMDMAndroidProfilesSummaryFunc(ctx, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMDMProfileSummaryFromHostCertificateTemplates(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
|
||||
s.mu.Lock()
|
||||
s.GetMDMProfileSummaryFromHostCertificateTemplatesFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.GetMDMProfileSummaryFromHostCertificateTemplatesFunc(ctx, teamID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetHostCertificateTemplates(ctx context.Context, hostUUID string) ([]fleet.HostCertificateTemplate, error) {
|
||||
s.mu.Lock()
|
||||
s.GetHostCertificateTemplatesFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.GetHostCertificateTemplatesFunc(ctx, hostUUID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetHostMDMAndroidProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAndroidProfile, error) {
|
||||
s.mu.Lock()
|
||||
s.GetHostMDMAndroidProfilesFuncInvoked = true
|
||||
|
||||
@@ -1363,6 +1363,14 @@ func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts f
|
||||
profiles = append(profiles, p.ToHostMDMProfile())
|
||||
}
|
||||
|
||||
hCertTemplates, err := svc.ds.GetHostCertificateTemplates(ctx, host.UUID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "get host certificate templates")
|
||||
}
|
||||
for _, ct := range hCertTemplates {
|
||||
profiles = append(profiles, ct.ToHostMDMProfile())
|
||||
}
|
||||
|
||||
case "darwin", "ios", "ipados":
|
||||
if ac.MDM.EnabledAndConfigured {
|
||||
profs, err := svc.ds.GetHostMDMAppleProfiles(ctx, host.UUID)
|
||||
|
||||
@@ -1032,7 +1032,12 @@ func (svc *Service) GetMDMAndroidProfilesSummary(ctx context.Context, teamID *ui
|
||||
return nil, ctxerr.Wrap(ctx, err)
|
||||
}
|
||||
|
||||
return ps, nil
|
||||
hcts, err := svc.ds.GetMDMProfileSummaryFromHostCertificateTemplates(ctx, teamID)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err)
|
||||
}
|
||||
|
||||
return ps.Add(hcts), nil
|
||||
}
|
||||
|
||||
// authorizeAllHostsTeams is a helper function that loads the hosts
|
||||
@@ -2443,7 +2448,6 @@ func getAndroidProfiles(ctx context.Context,
|
||||
appCfg *fleet.AppConfig,
|
||||
profiles map[int]fleet.MDMProfileBatchPayload,
|
||||
labelMap map[string]fleet.ConfigurationProfileLabel,
|
||||
// isPremium bool,
|
||||
) (map[int]*fleet.MDMAndroidConfigProfile, error) {
|
||||
profs := make(map[int]*fleet.MDMAndroidConfigProfile, len(profiles))
|
||||
for i, profile := range profiles {
|
||||
|
||||
@@ -676,6 +676,9 @@ func TestMDMCommonAuthorization(t *testing.T) {
|
||||
ds.GetConfigEnableDiskEncryptionFunc = func(ctx context.Context, teamID *uint) (fleet.DiskEncryptionConfig, error) {
|
||||
return fleet.DiskEncryptionConfig{}, nil
|
||||
}
|
||||
ds.GetMDMProfileSummaryFromHostCertificateTemplatesFunc = func(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
|
||||
return &fleet.MDMProfilesSummary{}, nil
|
||||
}
|
||||
|
||||
ds.AreHostsConnectedToFleetMDMFunc = func(ctx context.Context, hosts []*fleet.Host) (map[string]bool, error) {
|
||||
res := make(map[string]bool, len(hosts))
|
||||
|
||||
Reference in New Issue
Block a user