From 6fd06d648601edd89c01e25426e2e35ff2a8a37b Mon Sep 17 00:00:00 2001
From: Martin Angers
Date: Mon, 4 Dec 2023 08:41:37 -0500
Subject: [PATCH 01/12] Add back Windows Profiles documentation changes
(#15358)
---
.../configuration-files/README.md | 30 +++++++++++++++++++
docs/Contributing/API-for-contributors.md | 5 ++++
docs/REST API/rest-api.md | 15 ++++++++++
3 files changed, 50 insertions(+)
diff --git a/docs/Configuration/configuration-files/README.md b/docs/Configuration/configuration-files/README.md
index 0e7f9893ac..163d0935c0 100644
--- a/docs/Configuration/configuration-files/README.md
+++ b/docs/Configuration/configuration-files/README.md
@@ -249,6 +249,10 @@ spec:
- path/to/profile1.mobileconfig
- path/to/profile2.mobileconfig
enable_disk_encryption: true
+ windows_settings:
+ custom_settings:
+ - path/to/profile3.xml
+ - path/to/profile4.xml
scripts:
- path/to/script1.sh
- path/to/script2.sh
@@ -460,6 +464,10 @@ spec:
- path/to/profile1.mobileconfig
- path/to/profile2.mobileconfig
enable_disk_encryption: true
+ windows_settings:
+ custom_settings:
+ - path/to/profile3.xml
+ - path/to/profile4.xml
```
### Settings
@@ -1187,6 +1195,28 @@ If you're using Fleet Premium, this enforces disk encryption on all hosts assign
enable_disk_encryption: true
```
+##### mdm.windows_settings
+
+The following settings are Windows-specific settings for Fleet's MDM solution.
+
+##### mdm.windows_settings.custom_settings
+
+List of configuration profile files to apply to all hosts.
+
+If you're using Fleet Premium, these profiles apply to all hosts assigned to no team.
+
+> If you want to add profiles to all Windows hosts on a specific team in Fleet, use the `team` YAML document. Learn how to create one [here](#teams).
+
+- Default value: none
+- Config file format:
+ ```yaml
+ mdm:
+ windows_settings:
+ custom_settings:
+ - path/to/profile1.xml
+ - path/to/profile2.xml
+ ```
+
#### Scripts
List of saved scripts that can be run on all hosts.
diff --git a/docs/Contributing/API-for-contributors.md b/docs/Contributing/API-for-contributors.md
index 63f362478a..6cef6ff8c9 100644
--- a/docs/Contributing/API-for-contributors.md
+++ b/docs/Contributing/API-for-contributors.md
@@ -1283,6 +1283,8 @@ If the `name` is not already associated with an existing team, this API route cr
| mdm.macos_settings.custom_settings | list | body | The list of .mobileconfig files to apply to hosts that belong to this team. |
| scripts | list | body | A list of script files to add to this team so they can be executed at a later time. |
| mdm.macos_settings.enable_disk_encryption | bool | body | Whether disk encryption should be enabled for hosts that belong to this team. |
+| mdm.windows_settings | object | body | The Windows-specific MDM settings. |
+| mdm.windows_settings.custom_settings | list | body | The list of .xml files to apply to hosts that belong to this team. |
| force | bool | query | Force apply the spec even if there are (ignorable) validation errors. Those are unknown keys and agent options-related validations. |
| dry_run | bool | query | Validate the provided JSON for unknown keys and invalid value types and return any validation errors, but do not apply the changes. |
@@ -1342,6 +1344,9 @@ If the `name` is not already associated with an existing team, this API route cr
"macos_settings": {
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": true
+ },
+ "windows_settings": {
+ "custom_settings": ["path/to/profile2.xml"],
}
},
"scripts": ["path/to/script.sh"],
diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md
index 07932fb8b8..6cd0fbfee4 100644
--- a/docs/REST API/rest-api.md
+++ b/docs/REST API/rest-api.md
@@ -883,6 +883,9 @@ None.
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": true
},
+ "windows_settings": {
+ "custom_settings": ["path/to/profile2.xml"],
+ },
"scripts": ["path/to/script.sh"],
"end_user_authentication": {
"entity_id": "",
@@ -1084,6 +1087,7 @@ Modifies the Fleet's configuration with the supplied information.
| webhook_url | string | body | _mdm.macos_migration settings_. The webhook url configured to receive requests to unenroll devices migrating from your old MDM solution. **Requires Fleet Premium license** |
| custom_settings | list | body | _mdm.macos_settings settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have those custom profiles applied. |
| enable_disk_encryption | boolean | body | _mdm.macos_settings settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have disk encryption enabled if set to true. **Requires Fleet Premium license** |
+| custom_settings | list | body | _mdm.windows_settings settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have those custom profiles applied. |
| scripts | list | body | A list of script files to add so they can be executed at a later time. |
| enable_end_user_authentication | boolean | body | _mdm.macos_setup settings_. If set to true, end user authentication will be required during automatic MDM enrollment of new macOS devices. Settings for your IdP provider must also be [configured](https://fleetdm.com/docs/using-fleet/mdm-macos-setup-experience#end-user-authentication-and-eula). **Requires Fleet Premium license** |
| additional_queries | boolean | body | Whether or not additional queries are enabled on hosts. |
@@ -1178,6 +1182,9 @@ Note that when making changes to the `integrations` object, all integrations mus
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": true
},
+ "windows_settings": {
+ "custom_settings": ["path/to/profile2.xml"],
+ },
"end_user_authentication": {
"entity_id": "",
"issuer_uri": "",
@@ -7382,6 +7389,9 @@ _Available in Fleet Premium_
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": false
},
+ "windows_settings": {
+ "custom_settings": ["path/to/profile2.xml"],
+ },
"macos_setup": {
"bootstrap_package": "",
"enable_end_user_authentication": false,
@@ -7498,6 +7508,8 @@ _Available in Fleet Premium_
| macos_settings | object | body | macOS-specific settings. |
| custom_settings | list | body | The list of .mobileconfig files to apply to macOS hosts that belong to this team. |
| enable_disk_encryption | boolean | body | Hosts that belong to this team and are enrolled into Fleet's MDM will have disk encryption enabled if set to true. |
+| windows_settings | object | body | Windows-specific settings. |
+| custom_settings | list | body | The list of XML files to apply to Windows hosts that belong to this team. |
| macos_setup | object | body | Setup for automatic MDM enrollment of macOS hosts. |
| enable_end_user_authentication | boolean | body | If set to true, end user authentication will be required during automatic MDM enrollment of new macOS hosts. Settings for your IdP provider must also be [configured](https://fleetdm.com/docs/using-fleet/mdm-macos-setup-experience#end-user-authentication-and-eula). |
@@ -7563,6 +7575,9 @@ _Available in Fleet Premium_
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": false
},
+ "windows_settings": {
+ "custom_settings": ["path/to/profile2.xml"],
+ },
"macos_setup": {
"bootstrap_package": "",
"enable_end_user_authentication": false,
From a7be0be9e9517a23a8d80b5fd8b810e7e59b54a8 Mon Sep 17 00:00:00 2001
From: Roberto Dip
Date: Mon, 4 Dec 2023 11:03:05 -0300
Subject: [PATCH 02/12] improve mdm_windows query to account for multiple
registry entries (#15391)
for #15362, this adjusts the query we use to get MDM details for windows
to account for hosts that might have more than one matching value in the
registry for any of the items we query.
---
changes/15362-windows-mdm-query | 1 +
server/service/osquery_utils/queries.go | 78 +++++++++++++-------
server/service/osquery_utils/queries_test.go | 53 +++++++------
3 files changed, 85 insertions(+), 47 deletions(-)
create mode 100644 changes/15362-windows-mdm-query
diff --git a/changes/15362-windows-mdm-query b/changes/15362-windows-mdm-query
new file mode 100644
index 0000000000..86a5c269ed
--- /dev/null
+++ b/changes/15362-windows-mdm-query
@@ -0,0 +1 @@
+* Improved the query used to get MDM details for Windows hosts to account for multiple registry entries.
diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go
index 9dfc02aa4d..b213cf0421 100644
--- a/server/service/osquery_utils/queries.go
+++ b/server/service/osquery_utils/queries.go
@@ -438,31 +438,48 @@ var extraDetailQueries = map[string]DetailQuery{
Discovery: discoveryTable("mdm"),
},
"mdm_windows": {
+ // we get most of the MDM information for Windows from the
+ // `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\%%`
+ // registry keys. A computer might many different folders under
+ // that path, for different enrollments, so we need to group by
+ // enrollment (key in this case) and try to grab the most
+ // likely candiate to be an MDM solution.
+ //
+ // The best way I have found, is to filter by groups of entries
+ // with an UPN value, and pick the first one.
+ //
+ // An example of a host having more than one entry: when
+ // the `mdm_bridge` table is used, the `mdmlocalmanagement.dll`
+ // registers an MDM with ProviderID = `Local_Management`
+ //
+ // For more information, refer to issue #15362
Query: `
- SELECT * FROM (
- SELECT "provider_id" AS "key", data as "value" FROM registry
- WHERE path LIKE 'HKEY_LOCAL_MACHINE\Software\Microsoft\Enrollments\%\ProviderID'
- LIMIT 1
+ WITH registry_keys AS (
+ SELECT *
+ FROM registry
+ WHERE path LIKE 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\%%'
+ ),
+ enrollment_info AS (
+ SELECT
+ MAX(CASE WHEN name = 'UPN' THEN data END) AS upn,
+ MAX(CASE WHEN name = 'IsFederated' THEN data END) AS is_federated,
+ MAX(CASE WHEN name = 'DiscoveryServiceFullURL' THEN data END) AS discovery_service_url,
+ MAX(CASE WHEN name = 'ProviderID' THEN data END) AS provider_id
+ FROM registry_keys
+ GROUP BY key
)
- UNION ALL
- SELECT * FROM (
- SELECT "discovery_service_url" AS "key", data as "value" FROM registry
- WHERE path LIKE 'HKEY_LOCAL_MACHINE\Software\Microsoft\Enrollments\%\DiscoveryServiceFullURL'
- LIMIT 1
- )
- UNION ALL
- SELECT * FROM (
- SELECT "is_federated" AS "key", data as "value" FROM registry
- WHERE path LIKE 'HKEY_LOCAL_MACHINE\Software\Microsoft\Enrollments\%\IsFederated'
- LIMIT 1
- )
- UNION ALL
- SELECT * FROM (
- SELECT "installation_type" AS "key", data as "value" FROM registry
- WHERE path = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallationType'
- LIMIT 1
- )
- ;
+ SELECT
+ e.is_federated,
+ e.discovery_service_url,
+ e.provider_id,
+ (
+ SELECT data
+ FROM registry
+ WHERE path = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallationType'
+ ) AS installation_type
+ FROM enrollment_info e
+ WHERE e.upn IS NOT NULL
+ LIMIT 1;
`,
DirectIngestFunc: directIngestMDMWindows,
Platforms: []string{"windows"},
@@ -1458,10 +1475,19 @@ func deduceMDMNameWindows(data map[string]string) string {
}
func directIngestMDMWindows(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string) error {
- data := make(map[string]string, len(rows))
- for _, r := range rows {
- data[r["key"]] = r["value"]
+ if len(rows) != 1 {
+ logger.Log("component", "service", "method", "directIngestMDMWindows", "warn",
+ fmt.Sprintf("mdm expected single result got %d", len(rows)))
+ // assume the extension is not there
+ return nil
}
+
+ if len(rows) > 1 {
+ logger.Log("component", "service", "method", "directIngestMDMWindows", "warn",
+ fmt.Sprintf("mdm expected single result got %d", len(rows)))
+ }
+
+ data := rows[0]
var enrolled bool
var automatic bool
serverURL := data["discovery_service_url"]
diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go
index 1e32c22bb3..6d42d4dae6 100644
--- a/server/service/osquery_utils/queries_test.go
+++ b/server/service/osquery_utils/queries_test.go
@@ -588,10 +588,12 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "off empty server URL",
data: []map[string]string{
- {"key": "discovery_service_url", "value": ""},
- {"key": "is_federated", "value": "1"},
- {"key": "provider_id", "value": "Some_ID"},
- {"key": "installation_type", "value": "Client"},
+ {
+ "discovery_service_url": "",
+ "is_federated": "1",
+ "provider_id": "Some_ID",
+ "installation_type": "Client",
+ },
},
wantEnrolled: false,
wantInstalledFromDep: false,
@@ -601,8 +603,10 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "off missing is_federated and server url",
data: []map[string]string{
- {"key": "provider_id", "value": "Some_ID"},
- {"key": "installation_type", "value": "Client"},
+ {
+ "provider_id": "Some_ID",
+ "installation_type": "Client",
+ },
},
wantEnrolled: false,
wantInstalledFromDep: false,
@@ -612,10 +616,12 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "on automatic",
data: []map[string]string{
- {"key": "discovery_service_url", "value": "https://example.com"},
- {"key": "is_federated", "value": "1"},
- {"key": "provider_id", "value": "Some_ID"},
- {"key": "installation_type", "value": "Client"},
+ {
+ "discovery_service_url": "https://example.com",
+ "is_federated": "1",
+ "provider_id": "Some_ID",
+ "installation_type": "Client",
+ },
},
wantEnrolled: true,
wantInstalledFromDep: true,
@@ -625,10 +631,12 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "on manual",
data: []map[string]string{
- {"key": "discovery_service_url", "value": "https://example.com"},
- {"key": "is_federated", "value": "0"},
- {"key": "provider_id", "value": "Local_Management"},
- {"key": "installation_type", "value": "Client"},
+ {
+ "discovery_service_url": "https://example.com",
+ "is_federated": "0",
+ "provider_id": "Local_Management",
+ "installation_type": "Client",
+ },
},
wantEnrolled: true,
wantInstalledFromDep: false,
@@ -638,9 +646,11 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "on manual missing is_federated",
data: []map[string]string{
- {"key": "discovery_service_url", "value": "https://example.com"},
- {"key": "provider_id", "value": "Some_ID"},
- {"key": "installation_type", "value": "Client"},
+ {
+ "discovery_service_url": "https://example.com",
+ "provider_id": "Some_ID",
+ "installation_type": "Client",
+ },
},
wantEnrolled: true,
wantInstalledFromDep: false,
@@ -650,10 +660,11 @@ func TestDirectIngestMDMWindows(t *testing.T) {
{
name: "is_server",
data: []map[string]string{
- {"key": "discovery_service_url", "value": "https://example.com"},
- {"key": "is_federated", "value": "1"},
- {"key": "provider_id", "value": "Some_ID"},
- {"key": "installation_type", "value": "Windows SeRvEr 99.9"},
+ {
+ "discovery_service_url": "https://example.com",
+ "is_federated": "1",
+ "provider_id": "Some_ID",
+ "installation_type": "Windows SeRvEr 99.9"},
},
wantEnrolled: true,
wantInstalledFromDep: true,
From ab0700b4e0edbee93c52add0b418cf509f9a46e7 Mon Sep 17 00:00:00 2001
From: Lucas Manuel Rodriguez
Date: Mon, 4 Dec 2023 11:18:49 -0300
Subject: [PATCH 03/12] Fix osquery result logging when queries are configured
outside of Fleet (#15393)
#15168
- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Added/updated tests.
- [X] Manual QA for all new/changed functionality.
The issue can be reproduced by running `osqueryd` with
`--config_plugin=filesystem --config_path=/path/to/config.json`
This means the osquery config is fetched from a file rather than from
Fleet's agent settings.
The `/path/to/config.json` has the agent settings, e.g.:
```
{
"decorators": {
"load": [
"SELECT uuid AS host_uuid FROM system_info;",
"SELECT hostname AS hostname FROM system_info;"
]
},
"options": {
"disable_distributed": false,
"distributed_interval": 10,
"distributed_plugin": "tls",
"distributed_tls_max_attempts": 3,
"logger_tls_endpoint": "/api/osquery/log",
"logger_tls_period": 10,
"pack_delimiter": "/"
},
"schedule": {
"USB devices": {
"query": "SELECT * FROM usb_devices;",
"interval": 15
},
"OS version": {
"query": "SELECT * FROM os_version;",
"interval": 10
}
},
"packs": {
"Elsewhere": {
"queries": {
"Osquery Info": {
"query": "SELECT * FROM osquery_info;",
"interval": 30,
"platform": "",
"version": "",
"snapshot": true
}
}
}
}
}
```
The three queries should be logged to Fleet's configured result logging
destination (default is `filesystem`).
---
changes/15168-scheduled-outside-fleet | 1 +
server/service/osquery.go | 73 ++++++++++++++++++++-------
server/service/osquery_test.go | 60 ++++++++++++----------
3 files changed, 88 insertions(+), 46 deletions(-)
create mode 100644 changes/15168-scheduled-outside-fleet
diff --git a/changes/15168-scheduled-outside-fleet b/changes/15168-scheduled-outside-fleet
new file mode 100644
index 0000000000..4b469ab252
--- /dev/null
+++ b/changes/15168-scheduled-outside-fleet
@@ -0,0 +1 @@
+* Fixed logging of results for scheduled queries configured outside of Fleet, when `server_settings.query_reports_disabled` is set to `true`.
diff --git a/server/service/osquery.go b/server/service/osquery.go
index 08f8ab970d..0d9661e901 100644
--- a/server/service/osquery.go
+++ b/server/service/osquery.go
@@ -1402,15 +1402,44 @@ func submitLogsEndpoint(ctx context.Context, request interface{}, svc fleet.Serv
return submitLogsResponse{Err: err}, nil
}
-func (svc *Service) preProcessOsqueryResults(ctx context.Context, osqueryResults []json.RawMessage, queryReportsDisabled bool) (unmarshaledResults []*fleet.ScheduledQueryResult, queriesDBData map[string]*fleet.Query) {
+// preProcessOsqueryResults will attempt to unmarshal `osqueryResults` and will return:
+// - `unmarshaledResults` with each result unmarshaled to `fleet.ScheduledQueryResult`s, where if an item is `nil` it means the corresponding
+// `osqueryResults` item could not be unmarshaled.
+// - queriesDBData has the corresponding DB query to each unmarshalled result in `osqueryResults`.
+//
+// If queryReportsDisabled is true then it returns only t he `unmarshaledResults` without querying the DB.
+func (svc *Service) preProcessOsqueryResults(
+ ctx context.Context,
+ osqueryResults []json.RawMessage,
+ queryReportsDisabled bool,
+) (unmarshaledResults []*fleet.ScheduledQueryResult, queriesDBData map[string]*fleet.Query) {
// skipauth: Authorization is currently for user endpoints only.
svc.authz.SkipAuthorization(ctx)
+ lograw := func(raw json.RawMessage) string {
+ logr := raw
+ if len(raw) >= 64 {
+ logr = raw[:64]
+ }
+ return string(logr)
+ }
+
for _, raw := range osqueryResults {
var result *fleet.ScheduledQueryResult
if err := json.Unmarshal(raw, &result); err != nil {
- level.Error(svc.logger).Log("msg", "unmarshalling result", "err", err)
- // Note we store a nil item if the result could not be unmarshaled.
+ level.Debug(svc.logger).Log("msg", "unmarshalling result", "err", err, "result", lograw(raw))
+ // Note that if err != nil we have two scenarios:
+ // - result == nil: which means the result could not be unmarshalled, e.g. not JSON.
+ // - result != nil: which means that the result was (partially) unmarshalled but some specific
+ // field could not be unmarshalled.
+ //
+ // In both scenarios we want to add `result` to `unmarshaledResults`.
+ } else {
+ // If the unmarshaled result doesn't have a "name" field then we ignore the result.
+ if result != nil && result.QueryName == "" {
+ level.Debug(svc.logger).Log("msg", "missing name field", "result", lograw(raw))
+ result = nil
+ }
}
unmarshaledResults = append(unmarshaledResults, result)
}
@@ -1427,7 +1456,7 @@ func (svc *Service) preProcessOsqueryResults(ctx context.Context, osqueryResults
}
teamID, queryName, err := getQueryNameAndTeamIDFromResult(queryResult.QueryName)
if err != nil {
- level.Error(svc.logger).Log("msg", "querying name and team ID from result", "err", err)
+ level.Debug(svc.logger).Log("msg", "querying name and team ID from result", "err", err)
continue
}
if _, ok := queriesDBData[queryResult.QueryName]; ok {
@@ -1463,6 +1492,9 @@ func (svc *Service) SubmitResultLogs(ctx context.Context, logs []json.RawMessage
// otherwise the results will never clear from its local DB and
// will keep retrying forever.
//
+ // We do return errors if we fail to write to the external logging destination,
+ // so that the logs are not lost and osquery retries on its next log interval.
+ //
var queryReportsDisabled bool
appConfig, err := svc.ds.AppConfig(ctx)
@@ -1486,32 +1518,35 @@ func (svc *Service) SubmitResultLogs(ctx context.Context, logs []json.RawMessage
// Ignore results that could not be unmarshaled.
continue
}
+
if queryReportsDisabled {
- // If query_reports_disabled=true we write the logs
- // to the logging destination without any extra processing.
+ // If query_reports_disabled=true we write the logs to the logging destination without any extra processing.
//
- // If a query has automations_enabled = 0 we may still write the results for it here.
- // Eventually the query will be removed from the host schedule and thus Fleet
- // won't receive any further results anymore (/api/v1/osquery/config).
+ // If a query was recently configured with automations_enabled = 0 we may still write
+ // the results for it here. Eventually the query will be removed from the host schedule
+ // and thus Fleet won't receive any further results anymore.
filteredLogs = append(filteredLogs, logs[i])
continue
}
- //
- // If query_reports_disabled=false then we need to filter
- // the queries that have automations_enabled=false because
- // the query might have been scheduled because of discard_data=false.
- //
-
dbQuery, ok := queriesDBData[unmarshaledResult.QueryName]
if !ok {
- // Ignore results for unknown queries.
+ // If Fleet doesn't know of the query we write the logs to the logging destination
+ // without any extra processing. This is to support osquery nodes that load their
+ // config from elsewhere (e.g. using `--config_plugin=filesystem`).
+ //
+ // If a query was configured from Fleet but was recently removed, we may still write
+ // the results for it here. Eventually the query will be removed from the host schedule
+ // and thus Fleet won't receive any further results anymore.
+ filteredLogs = append(filteredLogs, logs[i])
continue
}
+
if !dbQuery.AutomationsEnabled {
// Ignore results for queries that have automations disabled.
continue
}
+
filteredLogs = append(filteredLogs, logs[i])
}
@@ -1646,7 +1681,7 @@ func getQueryNameAndTeamIDFromResult(path string) (*uint, string, error) {
if strings.HasPrefix(path, "pack/team-") {
parts := strings.SplitN(path, "/", 3)
if len(parts) != 3 {
- return nil, "", fmt.Errorf("unknown format: %s", path)
+ return nil, "", fmt.Errorf("unknown format: %q", path)
}
teamNumberStr := strings.TrimPrefix(parts[1], "team-")
@@ -1663,11 +1698,11 @@ func getQueryNameAndTeamIDFromResult(path string) (*uint, string, error) {
if strings.HasPrefix(path, "pack/") {
parts := strings.SplitN(path, "/", 3)
if len(parts) != 3 {
- return nil, "", fmt.Errorf("unknown format: %s", path)
+ return nil, "", fmt.Errorf("unknown format: %q", path)
}
return nil, parts[2], nil
}
// If none of the above patterns match, return error
- return nil, "", fmt.Errorf("unknown format: %s", path)
+ return nil, "", fmt.Errorf("unknown format: %q", path)
}
diff --git a/server/service/osquery_test.go b/server/service/osquery_test.go
index 78107ce698..9566721faa 100644
--- a/server/service/osquery_test.go
+++ b/server/service/osquery_test.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
+ "os"
"reflect"
"sort"
"strconv"
@@ -533,7 +534,7 @@ func TestSubmitStatusLogs(t *testing.T) {
func TestSubmitResultLogs(t *testing.T) {
ds := new(mock.Store)
- svc, ctx := newTestService(t, ds, nil, nil)
+ svc, ctx := newTestService(t, ds, nil, nil, &TestServerOpts{Logger: log.NewJSONLogger(os.Stdout)})
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{}, nil
@@ -614,60 +615,65 @@ func TestSubmitResultLogs(t *testing.T) {
serv.osqueryLogWriter = &OsqueryLogger{Result: testLogger}
validLogResults := []string{
- `{"name":"pack/Global/system_info","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 17:55:15 2016 UTC","unixTime":"1475258115","decorations":{"host_uuid":"some_uuid","username":"zwass"},"columns":{"cpu_brand":"Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz","hostname":"hostimus","physical_memory":"17179869184"},"action":"added"}`,
+ `{"name":"pack/Global/system_info","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 17:55:15 2016 UTC","unixTime":1475258115,"decorations":{"host_uuid":"some_uuid","username":"zwass"},"columns":{"cpu_brand":"Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz","hostname":"hostimus","physical_memory":"17179869184"},"action":"added"}`,
- `{"name":"pack/SomePack/encrypted","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 21:19:15 2016 UTC","unixTime":"1475270355","decorations":{"host_uuid":"4740D59F-699E-5B29-960B-979AAF9BBEEB","username":"zwass"},"columns":{"encrypted":"1","name":"\/dev\/disk1","type":"AES-XTS","uid":"","user_uuid":"","uuid":"some_uuid"},"action":"added"}`,
- `{"name":"pack/SomePack/encrypted","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 21:19:14 2016 UTC","unixTime":"1475270354","decorations":{"host_uuid":"4740D59F-699E-5B29-960B-979AAF9BBEEB","username":"zwass"},"columns":{"encrypted":"1","name":"\/dev\/disk1","type":"AES-XTS","uid":"","user_uuid":"","uuid":"some_uuid"},"action":"added"}`,
+ `{"name":"pack/SomePack/encrypted","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 21:19:15 2016 UTC","unixTime":1475270355,"decorations":{"host_uuid":"4740D59F-699E-5B29-960B-979AAF9BBEEB","username":"zwass"},"columns":{"encrypted":"1","name":"\/dev\/disk1","type":"AES-XTS","uid":"","user_uuid":"","uuid":"some_uuid"},"action":"added"}`,
+ `{"name":"pack/SomePack/encrypted","hostIdentifier":"some_uuid","calendarTime":"Fri Sep 30 21:19:14 2016 UTC","unixTime":1475270354,"decorations":{"host_uuid":"4740D59F-699E-5B29-960B-979AAF9BBEEB","username":"zwass"},"columns":{"encrypted":"1","name":"\/dev\/disk1","type":"AES-XTS","uid":"","user_uuid":"","uuid":"some_uuid"},"action":"added"}`,
// These results belong to the same query but have 1 second difference.
`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/time","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/time","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:50 2017 UTC","unixTime":1484078930,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/time","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:52 2017 UTC","unixTime":1484078932,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
- `{"diffResults":{"removed":[{"address":"127.0.0.1","hostnames":"kl.groob.io"}],"added":""},"name":"pack\/team-1/hosts","hostIdentifier":"FA01680E-98CA-5557-8F59-7716ECFEE964","calendarTime":"Sun Nov 19 00:02:08 2017 UTC","unixTime":"1511049728","epoch":"0","counter":"10","decorations":{"host_uuid":"FA01680E-98CA-5557-8F59-7716ECFEE964","hostname":"kl.groob.io"}}`,
+ `{"diffResults":{"removed":[{"address":"127.0.0.1","hostnames":"kl.groob.io"}],"added":""},"name":"pack\/team-1/hosts","hostIdentifier":"FA01680E-98CA-5557-8F59-7716ECFEE964","calendarTime":"Sun Nov 19 00:02:08 2017 UTC","unixTime":1511049728,"epoch":"0","counter":"10","decorations":{"host_uuid":"FA01680E-98CA-5557-8F59-7716ECFEE964","hostname":"kl.groob.io"}}`,
`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/query_should_be_saved_and_submitted","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
- //`{"snapshot":[],"action":"snapshot","name":"pack/Global/query_no_rows","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
+ // Fleet doesn't know of this query, so this result should be streamed as is (This is to support streaming results for osquery nodes that are configured outside of Fleet, e.g. `--config_plugin=filesystem`).
+ `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/doesntexist","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
+
+ // The "name" field has invalid format, so this result will be streamed as is (This is to support streaming results for osquery nodes that are configured outside of Fleet, e.g. `--config_plugin=filesystem`).
+ `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"some_name","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
+ `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-foo/bar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
+ `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
+ `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/PackName","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`,
}
logJSON := fmt.Sprintf("[%s]", strings.Join(validLogResults, ","))
resultWithInvalidJSON := []byte("foobar:\n\t123")
+ resultWithInvalidJSONLong := []byte("foobar:\n\t1233333333333333333333333333333333333333333333333333333333")
// The "name" field will be empty, so this result will be ignored.
resultWithoutName := []byte(`{"unknown":{"foo": [] }}`)
- // The "name" field has invalid format, so this result will be ignored.
- resultWithInvalidNameFmt1 := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-foo/bar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":"1484078931","decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
- resultWithInvalidNameFmt2 := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":"1484078931","decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
- resultWithInvalidNameFmt3 := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/PackName","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":"1484078931","decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
- // The query doesn't exist, so this result will be ignored.
- resultWithQueryDoesNotExist := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/doesntexist","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":"1484078931","decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
// The query was configured with automations disabled, so this result will be ignored.
- resultWithQueryNotAutomated := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/query_not_automated","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":"1484078931","decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
+ resultWithQueryNotAutomated := []byte(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/Global/query_not_automated","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
// The query is supposed to be saved but with automations disabled (and has two columns).
resultWithQuerySavedNotAutomated := []byte(`{"snapshot":[{"hour":"20","minutes":"8"},{"hour":"21","minutes":"9"}],"action":"snapshot","name":"pack/Global/query_should_be_saved_but_not_submitted","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`)
- var results []json.RawMessage
- err := json.Unmarshal([]byte(logJSON), &results)
+ var validResults []json.RawMessage
+ err := json.Unmarshal([]byte(logJSON), &validResults)
require.NoError(t, err)
host := fleet.Host{
ID: 999,
}
ctx = hostctx.NewContext(ctx, &host)
- // Submit valid and invalid log results mixed.
- err = serv.SubmitResultLogs(ctx, append(append(results[:3],
- resultWithInvalidJSON,
- resultWithoutName,
- resultWithInvalidNameFmt1,
- resultWithInvalidNameFmt2,
- resultWithInvalidNameFmt3,
- resultWithQueryDoesNotExist,
- resultWithQueryNotAutomated,
- resultWithQuerySavedNotAutomated,
- ), results[3:]...))
+
+ // Submit valid, invalid and to-be-ignored log results mixed.
+ validAndInvalidResults := make([]json.RawMessage, 0, len(validResults)+5)
+ for i, result := range validResults {
+ validAndInvalidResults = append(validAndInvalidResults, result)
+ if i == 2 {
+ validAndInvalidResults = append(validAndInvalidResults,
+ resultWithInvalidJSON, resultWithInvalidJSONLong,
+ resultWithoutName, resultWithQueryNotAutomated,
+ resultWithQuerySavedNotAutomated,
+ )
+ }
+ }
+ err = serv.SubmitResultLogs(ctx, validAndInvalidResults)
require.NoError(t, err)
- assert.Equal(t, results, testLogger.logs)
+ assert.Equal(t, validResults, testLogger.logs)
}
func TestSaveResultLogsToQueryReports(t *testing.T) {
From 88612cdf8ceed48e7a7a3542f67fdcda94cc8caa Mon Sep 17 00:00:00 2001
From: Martin Angers
Date: Mon, 4 Dec 2023 09:50:06 -0500
Subject: [PATCH 04/12] Add back Windows OS Updates documentation changes
(#15363)
---
docs/REST API/rest-api.md | 21 +++++++++++++++++++++
docs/Using Fleet/Audit-logs.md | 21 +++++++++++++++++++++
2 files changed, 42 insertions(+)
diff --git a/docs/REST API/rest-api.md b/docs/REST API/rest-api.md
index 6cd0fbfee4..282be6eafa 100644
--- a/docs/REST API/rest-api.md
+++ b/docs/REST API/rest-api.md
@@ -879,6 +879,10 @@ None.
"minimum_version": "12.3.1",
"deadline": "2022-01-01"
},
+ "windows_updates": {
+ "deadline_days": 5,
+ "grace_period_days": 1
+ },
"macos_settings": {
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": true
@@ -1082,6 +1086,8 @@ Modifies the Fleet's configuration with the supplied information.
| windows_enabled_and_configured | boolean | body | _mdm settings_. Enables Windows MDM support. |
| minimum_version | string | body | _mdm.macos_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will be nudged until their macOS is at or above this version. **Requires Fleet Premium license** |
| deadline | string | body | _mdm.macos_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. **Requires Fleet Premium license** |
+| deadline_days | integer | body | _mdm.windows_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have this number of days before updates are installed on Windows. **Requires Fleet Premium license** |
+| grace_period_days | integer | body | _mdm.windows_updates settings_. Hosts that belong to no team and are enrolled into Fleet's MDM will have this number of days before Windows restarts to install updates. **Requires Fleet Premium license** |
| enable | boolean | body | _mdm.macos_migration settings_. Whether to enable the end user migration workflow for devices migrating from your old MDM solution. **Requires Fleet Premium license** |
| mode | string | body | _mdm.macos_migration settings_. The end user migration workflow mode for devices migrating from your old MDM solution. Options are `"voluntary"` or `"forced"`. **Requires Fleet Premium license** |
| webhook_url | string | body | _mdm.macos_migration settings_. The webhook url configured to receive requests to unenroll devices migrating from your old MDM solution. **Requires Fleet Premium license** |
@@ -1178,6 +1184,10 @@ Note that when making changes to the `integrations` object, all integrations mus
"minimum_version": "12.3.1",
"deadline": "2022-01-01"
},
+ "windows_updates": {
+ "deadline_days": 5,
+ "grace_period_days": 1
+ },
"macos_settings": {
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": true
@@ -7385,6 +7395,10 @@ _Available in Fleet Premium_
"minimum_version": "12.3.1",
"deadline": "2022-01-01"
},
+ "windows_updates": {
+ "deadline_days": 5,
+ "grace_period_days": 1
+ },
"macos_settings": {
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": false
@@ -7505,6 +7519,9 @@ _Available in Fleet Premium_
| macos_updates | object | body | macOS updates settings. |
| minimum_version | string | body | Hosts that belong to this team and are enrolled into Fleet's MDM will be nudged until their macOS is at or above this version. |
| deadline | string | body | Hosts that belong to this team and are enrolled into Fleet's MDM won't be able to dismiss the Nudge window once this deadline is past. |
+| windows_updates | object | body | Windows updates settings. |
+| deadline_days | integer | body | Hosts that belong to this team and are enrolled into Fleet's MDM will have this number of days before updates are installed on Windows. |
+| grace_period_days | integer | body | Hosts that belong to this team and are enrolled into Fleet's MDM will have this number of days before Windows restarts to install updates. |
| macos_settings | object | body | macOS-specific settings. |
| custom_settings | list | body | The list of .mobileconfig files to apply to macOS hosts that belong to this team. |
| enable_disk_encryption | boolean | body | Hosts that belong to this team and are enrolled into Fleet's MDM will have disk encryption enabled if set to true. |
@@ -7571,6 +7588,10 @@ _Available in Fleet Premium_
"minimum_version": "12.3.1",
"deadline": "2022-01-01"
},
+ "windows_updates": {
+ "deadline_days": 5,
+ "grace_period_days": 1
+ },
"macos_settings": {
"custom_settings": ["path/to/profile1.mobileconfig"],
"enable_disk_encryption": false
diff --git a/docs/Using Fleet/Audit-logs.md b/docs/Using Fleet/Audit-logs.md
index 5f432e8c16..a27e15997c 100644
--- a/docs/Using Fleet/Audit-logs.md
+++ b/docs/Using Fleet/Audit-logs.md
@@ -610,6 +610,27 @@ This activity contains the following fields:
}
```
+## `edited_windows_updates`
+
+Generated when the Windows OS updates deadline or grace period is modified.
+
+This activity contains the following fields:
+- "team_id": The ID of the team that the Windows OS updates settings applies to, `null` if it applies to devices that are not in a team.
+- "team_name": The name of the team that the Windows OS updates settings applies to, `null` if it applies to devices that are not in a team.
+- "deadline_days": The number of days before updates are installed, `null` if the requirement was removed.
+- "grace_period_days": The number of days after the deadline before the host is forced to restart, `null` if the requirement was removed.
+
+#### Example
+
+```json
+{
+ "team_id": 3,
+ "team_name": "Workstations",
+ "deadline_days": 5,
+ "grace_period_days": 2
+}
+```
+
## read_host_disk_encryption_key
Generated when a user reads the disk encryption key for a host.
From f5f66ebe63226a88b439cc2f7d5cccf0dcdce655 Mon Sep 17 00:00:00 2001
From: Martin Angers
Date: Mon, 4 Dec 2023 10:04:06 -0500
Subject: [PATCH 05/12] Unify profiles database identifier to `profile_uuid`
for macOS and Windows profiles. (#15297)
---
changes/issue-15274-unify-profiles-uuid | 1 +
cmd/fleetctl/apply_test.go | 4 +-
cmd/fleetctl/get_test.go | 2 +-
cmd/fleetctl/hosts_test.go | 8 +-
ee/server/service/teams.go | 2 +-
frontend/__mocks__/hostMock.ts | 3 +-
frontend/__mocks__/mdmMock.ts | 2 +-
frontend/interfaces/mdm.ts | 5 +-
.../ProfileStatusAggregate.tsx | 1 +
.../cards/CustomSettings/CustomSettings.tsx | 5 +-
.../DeleteProfileModal/DeleteProfileModal.tsx | 4 +-
.../ProfileListItem/ProfileListItem.tsx | 2 +-
.../ManageControlsPage/Scripts/Scripts.tsx | 1 +
.../UploadedPackageView.tsx | 1 +
.../components/UploadList/UploadList.tsx | 8 +-
.../UploadedEulaView/UploadedEulaView.tsx | 1 +
frontend/pages/hosts/details/helpers.ts | 3 +-
frontend/services/entities/mdm.ts | 4 +-
frontend/utilities/endpoints.ts | 3 +-
server/datastore/mysql/apple_mdm.go | 178 +++++---
server/datastore/mysql/apple_mdm_test.go | 197 ++++----
server/datastore/mysql/hosts_test.go | 9 +-
server/datastore/mysql/labels_test.go | 5 +-
server/datastore/mysql/mdm.go | 45 +-
server/datastore/mysql/mdm_test.go | 423 +++++++++---------
server/datastore/mysql/microsoft_mdm.go | 5 +-
server/datastore/mysql/microsoft_mdm_test.go | 4 +-
...5427_AlterMacOSProfilesPrimaryKeyToUUID.go | 130 ++++++
...AlterMacOSProfilesPrimaryKeyToUUID_test.go | 142 ++++++
server/datastore/mysql/schema.sql | 18 +-
server/datastore/mysql/teams_test.go | 21 +-
server/datastore/mysql/testing_utils.go | 2 +-
server/fleet/apple_mdm.go | 15 +-
server/fleet/datastore.go | 30 +-
server/fleet/mdm.go | 49 +-
server/fleet/microsoft_mdm.go | 2 +-
server/fleet/service.go | 12 +-
server/fleet/windows_mdm.go | 2 +
server/mock/datastore_mock.go | 48 +-
server/service/apple_mdm.go | 105 +++--
server/service/apple_mdm_test.go | 99 ++--
server/service/client_profiles.go | 3 +
server/service/handler.go | 4 +-
server/service/hosts.go | 4 +-
server/service/hosts_test.go | 8 +-
server/service/integration_mdm_test.go | 112 +++--
server/service/mdm.go | 57 ++-
server/service/mdm_test.go | 6 +-
server/service/microsoft_mdm.go | 12 +-
server/service/teams_test.go | 2 +-
50 files changed, 1127 insertions(+), 682 deletions(-)
create mode 100644 changes/issue-15274-unify-profiles-uuid
create mode 100644 server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID.go
create mode 100644 server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID_test.go
diff --git a/changes/issue-15274-unify-profiles-uuid b/changes/issue-15274-unify-profiles-uuid
new file mode 100644
index 0000000000..8eaeafa4b1
--- /dev/null
+++ b/changes/issue-15274-unify-profiles-uuid
@@ -0,0 +1 @@
+* Changed the Apple profiles ID to be a prefixed UUID as is the case for Windows profiles. The Apple profiles are now a UUID prefixed with "a" and the Windows ones are prefixed with "w".
diff --git a/cmd/fleetctl/apply_test.go b/cmd/fleetctl/apply_test.go
index b3e9c04864..24324cc22e 100644
--- a/cmd/fleetctl/apply_test.go
+++ b/cmd/fleetctl/apply_test.go
@@ -164,7 +164,7 @@ func TestApplyTeamSpecs(t *testing.T) {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, hostUUIDs []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, hostUUIDs []string) error {
return nil
}
@@ -991,7 +991,7 @@ func TestApplyAsGitOps(t *testing.T) {
ds.BatchSetMDMProfilesFunc = func(ctx context.Context, tmID *uint, macProfiles []*fleet.MDMAppleConfigProfile, winProfiles []*fleet.MDMWindowsConfigProfile) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, hostUUIDs []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, hostUUIDs []string) error {
return nil
}
ds.GetMDMAppleSetupAssistantFunc = func(ctx context.Context, teamID *uint) (*fleet.MDMAppleSetupAssistant, error) {
diff --git a/cmd/fleetctl/get_test.go b/cmd/fleetctl/get_test.go
index c3898b5989..dbbe953bb3 100644
--- a/cmd/fleetctl/get_test.go
+++ b/cmd/fleetctl/get_test.go
@@ -2036,7 +2036,7 @@ func TestGetTeamsYAMLAndApply(t *testing.T) {
ds.BatchSetMDMProfilesFunc = func(ctx context.Context, tmID *uint, macProfiles []*fleet.MDMAppleConfigProfile, winProfiles []*fleet.MDMWindowsConfigProfile) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, uuids []string) error {
return nil
}
ds.BatchSetScriptsFunc = func(ctx context.Context, tmID *uint, scripts []*fleet.Script) error {
diff --git a/cmd/fleetctl/hosts_test.go b/cmd/fleetctl/hosts_test.go
index 3b24897d1d..ca1b81587b 100644
--- a/cmd/fleetctl/hosts_test.go
+++ b/cmd/fleetctl/hosts_test.go
@@ -42,7 +42,7 @@ func TestHostsTransferByHosts(t *testing.T) {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, uuids []string) error {
return nil
}
@@ -98,7 +98,7 @@ func TestHostsTransferByLabel(t *testing.T) {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, uuids []string) error {
return nil
}
@@ -153,7 +153,7 @@ func TestHostsTransferByStatus(t *testing.T) {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, uuids []string) error {
return nil
}
@@ -210,7 +210,7 @@ func TestHostsTransferByStatusAndSearchQuery(t *testing.T) {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, uuids []string) error {
return nil
}
diff --git a/ee/server/service/teams.go b/ee/server/service/teams.go
index 0f15072800..8168cf5597 100644
--- a/ee/server/service/teams.go
+++ b/ee/server/service/teams.go
@@ -513,7 +513,7 @@ func (svc *Service) DeleteTeam(ctx context.Context, teamID uint) error {
}
if len(hostIDs) > 0 {
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
diff --git a/frontend/__mocks__/hostMock.ts b/frontend/__mocks__/hostMock.ts
index 36c9699873..52acccd2f2 100644
--- a/frontend/__mocks__/hostMock.ts
+++ b/frontend/__mocks__/hostMock.ts
@@ -2,9 +2,10 @@ import { IHost } from "interfaces/host";
import { IHostMdmProfile } from "interfaces/mdm";
const DEFAULT_HOST_PROFILE_MOCK: IHostMdmProfile = {
- profile_id: 1,
+ profile_uuid: "123-abc",
name: "Test Profile",
operation_type: "install",
+ platform: "darwin",
status: "verified",
detail: "This is verified",
};
diff --git a/frontend/__mocks__/mdmMock.ts b/frontend/__mocks__/mdmMock.ts
index fa1a0f26dc..c102b083ca 100644
--- a/frontend/__mocks__/mdmMock.ts
+++ b/frontend/__mocks__/mdmMock.ts
@@ -15,7 +15,7 @@ export const createMockMdmSolution = (
};
const DEFAULT_MDM_PROFILE_DATA: IMdmProfile = {
- profile_id: 1,
+ profile_uuid: "123-abc",
team_id: 0,
name: "Test Profile",
platform: "darwin",
diff --git a/frontend/interfaces/mdm.ts b/frontend/interfaces/mdm.ts
index 4ea2d9dcc5..4bc135d4d7 100644
--- a/frontend/interfaces/mdm.ts
+++ b/frontend/interfaces/mdm.ts
@@ -58,7 +58,7 @@ export interface IMdmSummaryResponse {
type ProfilePlatform = "darwin" | "windows";
export interface IMdmProfile {
- profile_id: number | string; // string for windows profiles
+ profile_uuid: string;
team_id: number;
name: string;
platform: ProfilePlatform;
@@ -73,9 +73,10 @@ export type MdmProfileStatus = "verified" | "verifying" | "pending" | "failed";
export type ProfileOperationType = "remove" | "install";
export interface IHostMdmProfile {
- profile_id: number;
+ profile_uuid: string;
name: string;
operation_type: ProfileOperationType | null;
+ platform: ProfilePlatform;
status: MdmProfileStatus;
detail: string;
}
diff --git a/frontend/pages/ManageControlsPage/OSSettings/ProfileStatusAggregate/ProfileStatusAggregate.tsx b/frontend/pages/ManageControlsPage/OSSettings/ProfileStatusAggregate/ProfileStatusAggregate.tsx
index b4b489e36d..79d06064bb 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/ProfileStatusAggregate/ProfileStatusAggregate.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/ProfileStatusAggregate/ProfileStatusAggregate.tsx
@@ -80,6 +80,7 @@ const ProfileStatusAggregate = ({
return (
{
+ const onDeleteProfile = async (profileId: string) => {
try {
await mdmAPI.deleteProfile(profileId);
refetchProfiles();
@@ -134,6 +134,7 @@ const CustomSettings = ({
return (
<>
(
@@ -170,7 +171,7 @@ const CustomSettings = ({
{showDeleteProfileModal && selectedProfile.current && (
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/DeleteProfileModal/DeleteProfileModal.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/DeleteProfileModal/DeleteProfileModal.tsx
index 09f584bb27..40b2976d15 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/DeleteProfileModal/DeleteProfileModal.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/DeleteProfileModal/DeleteProfileModal.tsx
@@ -7,9 +7,9 @@ import Button from "components/buttons/Button";
interface DeleteProfileModalProps {
profileName: string;
- profileId: number | string;
+ profileId: string;
onCancel: () => void;
- onDelete: (profileId: number | string) => void;
+ onDelete: (profileId: string) => void;
}
const baseClass = "delete-profile-modal";
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/ProfileListItem.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/ProfileListItem.tsx
index a8d416d072..ef51975415 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/ProfileListItem.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileListItem/ProfileListItem.tsx
@@ -39,7 +39,7 @@ interface IProfileListItemProps {
const ProfileListItem = ({ profile, onDelete }: IProfileListItemProps) => {
const onClickDownload = async () => {
- const fileContent = await mdmAPI.downloadProfile(profile.profile_id);
+ const fileContent = await mdmAPI.downloadProfile(profile.profile_uuid);
const formatDate = format(new Date(), "yyyy-MM-dd");
const extension = profile.platform === "darwin" ? "mobileconfig" : "xml";
const filename = `${formatDate}_${profile.name}.${extension}`;
diff --git a/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx b/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
index 0e03bac9fc..0140fdc7f0 100644
--- a/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
+++ b/frontend/pages/ManageControlsPage/Scripts/Scripts.tsx
@@ -120,6 +120,7 @@ const Scripts = ({ router, currentPage, teamIdForApi }: IScriptsProps) => {
return (
<>
(
diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/components/UploadedPackageView/UploadedPackageView.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/components/UploadedPackageView/UploadedPackageView.tsx
index 49ae47eed9..db7b7d1d9e 100644
--- a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/components/UploadedPackageView/UploadedPackageView.tsx
+++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/components/UploadedPackageView/UploadedPackageView.tsx
@@ -34,6 +34,7 @@ const UploadedPackageView = ({
/>
(
JSX.Element; // TODO: Typings
ListItemComponent: (props: { listItem: any }) => JSX.Element; // TODO: types
@@ -10,6 +12,7 @@ interface IUploadListProps {
}
const UploadList = ({
+ keyAttribute,
listItems,
HeadingComponent,
ListItemComponent,
@@ -17,7 +20,10 @@ const UploadList = ({
}: IUploadListProps) => {
const items = listItems.map((listItem) => {
return (
-
+
);
diff --git a/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/EulaSection/components/UploadedEulaView/UploadedEulaView.tsx b/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/EulaSection/components/UploadedEulaView/UploadedEulaView.tsx
index 4030919cbc..2b9dea056c 100644
--- a/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/EulaSection/components/UploadedEulaView/UploadedEulaView.tsx
+++ b/frontend/pages/admin/IntegrationsPage/cards/AutomaticEnrollment/components/EulaSection/components/UploadedEulaView/UploadedEulaView.tsx
@@ -29,6 +29,7 @@ const UploadedEulaView = ({
/>
(
diff --git a/frontend/pages/hosts/details/helpers.ts b/frontend/pages/hosts/details/helpers.ts
index 1657722951..15a3b5230a 100644
--- a/frontend/pages/hosts/details/helpers.ts
+++ b/frontend/pages/hosts/details/helpers.ts
@@ -25,7 +25,8 @@ export const generateWinDiskEncryptionProfile = (
detail: string
): IHostMdmProfile => {
return {
- profile_id: 0, // This s the only type of profile that can have this number
+ profile_uuid: "0", // This s the only type of profile that can have this value
+ platform: "windows",
name: "Disk Encryption",
status: convertWinDiskEncryptionStatusToProfileStatus(diskEncryptionStatus),
detail,
diff --git a/frontend/services/entities/mdm.ts b/frontend/services/entities/mdm.ts
index 0919dd6fda..dae8e8d0dc 100644
--- a/frontend/services/entities/mdm.ts
+++ b/frontend/services/entities/mdm.ts
@@ -92,7 +92,7 @@ const mdmService = {
return sendRequest("POST", MDM_PROFILES, formData);
},
- downloadProfile: (profileId: number | string) => {
+ downloadProfile: (profileId: string) => {
const { MDM_PROFILE } = endpoints;
const path = `${MDM_PROFILE(profileId)}?${buildQueryStringFromParams({
alt: "media",
@@ -100,7 +100,7 @@ const mdmService = {
return sendRequest("GET", path);
},
- deleteProfile: (profileId: number | string) => {
+ deleteProfile: (profileId: string) => {
const { MDM_PROFILE } = endpoints;
return sendRequest("DELETE", MDM_PROFILE(profileId));
},
diff --git a/frontend/utilities/endpoints.ts b/frontend/utilities/endpoints.ts
index cff89aff23..f93487b2a2 100644
--- a/frontend/utilities/endpoints.ts
+++ b/frontend/utilities/endpoints.ts
@@ -50,8 +50,7 @@ export default {
// MDM profile endpoints
MDM_PROFILES: `/${API_VERSION}/fleet/mdm/profiles`,
- MDM_PROFILE: (id: number | string) =>
- `/${API_VERSION}/fleet/mdm/profiles/${id}`,
+ MDM_PROFILE: (id: string) => `/${API_VERSION}/fleet/mdm/profiles/${id}`,
MDM_UPDATE_APPLE_SETTINGS: `/${API_VERSION}/fleet/mdm/apple/settings`,
MDM_PROFILES_STATUS_SUMMARY: `/${API_VERSION}/fleet/mdm/profiles/summary`,
diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go
index 6bc57095a4..50c93879b0 100644
--- a/server/datastore/mysql/apple_mdm.go
+++ b/server/datastore/mysql/apple_mdm.go
@@ -23,10 +23,11 @@ import (
)
func (ds *Datastore) NewMDMAppleConfigProfile(ctx context.Context, cp fleet.MDMAppleConfigProfile) (*fleet.MDMAppleConfigProfile, error) {
+ profUUID := "a" + uuid.New().String()
stmt := `
INSERT INTO
- mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum)
-(SELECT ?, ?, ?, ?, UNHEX(MD5(?)) FROM DUAL WHERE
+ mdm_apple_configuration_profiles (profile_uuid, team_id, identifier, name, mobileconfig, checksum)
+(SELECT ?, ?, ?, ?, ?, UNHEX(MD5(?)) FROM DUAL WHERE
NOT EXISTS (
SELECT 1 FROM mdm_windows_configuration_profiles WHERE name = ? AND team_id = ?
)
@@ -37,7 +38,8 @@ INSERT INTO
teamID = *cp.TeamID
}
- res, err := ds.writer(ctx).ExecContext(ctx, stmt, teamID, cp.Identifier, cp.Name, cp.Mobileconfig, cp.Mobileconfig, cp.Name, teamID)
+ res, err := ds.writer(ctx).ExecContext(ctx, stmt,
+ profUUID, teamID, cp.Identifier, cp.Name, cp.Mobileconfig, cp.Mobileconfig, cp.Name, teamID)
if err != nil {
switch {
case isDuplicate(err):
@@ -59,6 +61,7 @@ INSERT INTO
id, _ := res.LastInsertId()
return &fleet.MDMAppleConfigProfile{
+ ProfileUUID: profUUID,
ProfileID: uint(id),
Identifier: cp.Identifier,
Name: cp.Name,
@@ -89,6 +92,7 @@ func formatErrorDuplicateConfigProfile(err error, cp *fleet.MDMAppleConfigProfil
func (ds *Datastore) ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint) ([]*fleet.MDMAppleConfigProfile, error) {
stmt := `
SELECT
+ profile_uuid,
profile_id,
team_id,
name,
@@ -123,9 +127,18 @@ ORDER BY name`
return res, nil
}
-func (ds *Datastore) GetMDMAppleConfigProfile(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
+func (ds *Datastore) GetMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
+ return ds.getMDMAppleConfigProfileByIDOrUUID(ctx, profileID, "")
+}
+
+func (ds *Datastore) GetMDMAppleConfigProfile(ctx context.Context, profileUUID string) (*fleet.MDMAppleConfigProfile, error) {
+ return ds.getMDMAppleConfigProfileByIDOrUUID(ctx, 0, profileUUID)
+}
+
+func (ds *Datastore) getMDMAppleConfigProfileByIDOrUUID(ctx context.Context, id uint, uuid string) (*fleet.MDMAppleConfigProfile, error) {
stmt := `
SELECT
+ profile_uuid,
profile_id,
team_id,
name,
@@ -137,13 +150,24 @@ SELECT
FROM
mdm_apple_configuration_profiles
WHERE
- profile_id=?`
+`
+ var arg any
+ if uuid != "" {
+ arg = uuid
+ stmt += `profile_uuid = ?`
+ } else {
+ arg = id
+ stmt += `profile_id = ?`
+ }
var res fleet.MDMAppleConfigProfile
- err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, profileID)
+ err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, arg)
if err != nil {
if err == sql.ErrNoRows {
- return nil, ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithID(profileID))
+ if uuid != "" {
+ return nil, ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithName(uuid))
+ }
+ return nil, ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithID(id))
}
return nil, ctxerr.Wrap(ctx, err, "get mdm apple config profile")
}
@@ -151,18 +175,35 @@ WHERE
return &res, nil
}
-func (ds *Datastore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error {
- res, err := ds.writer(ctx).ExecContext(ctx, `DELETE FROM mdm_apple_configuration_profiles WHERE profile_id=?`, profileID)
+func (ds *Datastore) DeleteMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) error {
+ return ds.deleteMDMAppleConfigProfileByIDOrUUID(ctx, profileID, "")
+}
+
+func (ds *Datastore) DeleteMDMAppleConfigProfile(ctx context.Context, profileUUID string) error {
+ return ds.deleteMDMAppleConfigProfileByIDOrUUID(ctx, 0, profileUUID)
+}
+
+func (ds *Datastore) deleteMDMAppleConfigProfileByIDOrUUID(ctx context.Context, id uint, uuid string) error {
+ var arg any
+ stmt := `DELETE FROM mdm_apple_configuration_profiles WHERE `
+ if uuid != "" {
+ arg = uuid
+ stmt += `profile_uuid = ?`
+ } else {
+ arg = id
+ stmt += `profile_id = ?`
+ }
+ res, err := ds.writer(ctx).ExecContext(ctx, stmt, arg)
if err != nil {
return ctxerr.Wrap(ctx, err)
}
- deleted, err := res.RowsAffected()
- if err != nil {
- return ctxerr.Wrap(ctx, err, "fetching delete mdm config profile query rows affected")
- }
+ deleted, _ := res.RowsAffected()
if deleted != 1 {
- return ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithID(profileID))
+ if uuid != "" {
+ return ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithName(uuid))
+ }
+ return ctxerr.Wrap(ctx, notFound("MDMAppleConfigProfile").WithID(id))
}
return nil
@@ -189,7 +230,7 @@ func (ds *Datastore) DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx context.
func (ds *Datastore) GetHostMDMAppleProfiles(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
stmt := fmt.Sprintf(`
SELECT
-profile_id,
+profile_uuid,
profile_name AS name,
profile_identifier AS identifier,
-- internally, a NULL status implies that the cron needs to pick up
@@ -1128,10 +1169,11 @@ WHERE
const insertNewOrEditedProfile = `
INSERT INTO
mdm_apple_configuration_profiles (
- team_id, identifier, name, mobileconfig, checksum
+ profile_uuid, team_id, identifier, name, mobileconfig, checksum
)
VALUES
- ( ?, ?, ?, ?, UNHEX(MD5(mobileconfig)) )
+ -- see https://stackoverflow.com/a/51393124/1094941
+ ( CONCAT('a', CONVERT(uuid() USING utf8mb4)), ?, ?, ?, ?, UNHEX(MD5(mobileconfig)) )
ON DUPLICATE KEY UPDATE
name = VALUES(name),
mobileconfig = VALUES(mobileconfig),
@@ -1276,14 +1318,14 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
const desiredStateStmt = `
SELECT
- ds.profile_id as profile_id,
+ ds.profile_uuid as profile_uuid,
ds.host_uuid as host_uuid,
ds.profile_identifier as profile_identifier,
ds.profile_name as profile_name,
ds.checksum as checksum
FROM (
SELECT
- macp.profile_id,
+ macp.profile_uuid,
h.uuid as host_uuid,
macp.identifier as profile_identifier,
macp.name as profile_name,
@@ -1294,12 +1336,12 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
WHERE h.platform = 'darwin' AND ne.enabled = 1 AND ne.type = 'Device' AND h.uuid IN (?)
) as ds
LEFT JOIN host_mdm_apple_profiles hmap
- ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.host_uuid
+ ON hmap.profile_uuid = ds.profile_uuid AND hmap.host_uuid = ds.host_uuid
WHERE
-- profile has been updated
( hmap.checksum != ds.checksum ) OR
-- profiles in A but not in B
- ( hmap.profile_id IS NULL AND hmap.host_uuid IS NULL ) OR
+ ( hmap.profile_uuid IS NULL AND hmap.host_uuid IS NULL ) OR
-- profiles in A and B but with operation type "remove"
( hmap.host_uuid IS NOT NULL AND ( hmap.operation_type = ? OR hmap.operation_type IS NULL ) )`
@@ -1319,7 +1361,7 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
const currentStateStmt = `
SELECT
- hmap.profile_id as profile_id,
+ hmap.profile_uuid as profile_uuid,
hmap.host_uuid as host_uuid,
hmap.profile_identifier as profile_identifier,
hmap.profile_name as profile_name,
@@ -1330,18 +1372,18 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
hmap.command_uuid as command_uuid
FROM (
SELECT
- h.uuid, macp.profile_id
+ h.uuid, macp.profile_uuid
FROM mdm_apple_configuration_profiles macp
JOIN hosts h ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0)
JOIN nano_enrollments ne ON ne.device_id = h.uuid
WHERE h.platform = 'darwin' AND ne.enabled = 1 AND ne.type = 'Device' AND h.uuid IN (?)
) as ds
RIGHT JOIN host_mdm_apple_profiles hmap
- ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.uuid
+ ON hmap.profile_uuid = ds.profile_uuid AND hmap.host_uuid = ds.uuid
WHERE
hmap.host_uuid IN (?)
-- profiles that are in B but not in A
- AND ds.profile_id IS NULL AND ds.uuid IS NULL
+ AND ds.profile_uuid IS NULL AND ds.uuid IS NULL
-- except "remove" operations in any state
AND ( hmap.operation_type IS NULL OR hmap.operation_type != ? )
`
@@ -1395,7 +1437,7 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
executeUpsertBatch := func(valuePart string, args []any) error {
baseStmt := fmt.Sprintf(`
INSERT INTO host_mdm_apple_profiles (
- profile_id,
+ profile_uuid,
host_uuid,
profile_identifier,
profile_name,
@@ -1439,7 +1481,7 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
for _, p := range wantedProfiles {
if pp, ok := profileIntersection.GetMatchingProfileInCurrentState(p); ok {
if pp.Status != &fleet.MDMDeliveryFailed && bytes.Equal(pp.Checksum, p.Checksum) {
- pargs = append(pargs, p.ProfileID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
+ pargs = append(pargs, p.ProfileUUID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
pp.OperationType, pp.Status, pp.CommandUUID, pp.Detail)
psb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?),")
batchCount++
@@ -1454,7 +1496,7 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
}
}
- pargs = append(pargs, p.ProfileID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
+ pargs = append(pargs, p.ProfileUUID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
fleet.MDMOperationTypeInstall, nil, "", "")
psb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?),")
batchCount++
@@ -1471,7 +1513,7 @@ func (ds *Datastore) bulkSetPendingMDMAppleHostProfilesDB(
if _, ok := profileIntersection.GetMatchingProfileInDesiredState(p); ok {
continue
}
- pargs = append(pargs, p.ProfileID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
+ pargs = append(pargs, p.ProfileUUID, p.HostUUID, p.ProfileIdentifier, p.ProfileName, p.Checksum,
fleet.MDMOperationTypeRemove, nil, "", "")
psb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?),")
batchCount++
@@ -1524,30 +1566,30 @@ func (ds *Datastore) ListMDMAppleProfilesToInstall(ctx context.Context) ([]*flee
// be marked as status NULL so that it gets re-installed.
query := `
SELECT
- ds.profile_id,
- ds.host_uuid,
- ds.profile_identifier,
- ds.profile_name,
- ds.checksum
+ ds.profile_uuid,
+ ds.host_uuid,
+ ds.profile_identifier,
+ ds.profile_name,
+ ds.checksum
FROM (
SELECT
- macp.profile_id,
+ macp.profile_uuid,
h.uuid as host_uuid,
macp.identifier as profile_identifier,
macp.name as profile_name,
- macp.checksum as checksum
+ macp.checksum as checksum
FROM mdm_apple_configuration_profiles macp
JOIN hosts h ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0)
JOIN nano_enrollments ne ON ne.device_id = h.uuid
WHERE h.platform = 'darwin' AND ne.enabled = 1 AND ne.type = 'Device'
) as ds
LEFT JOIN host_mdm_apple_profiles hmap
- ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.host_uuid
+ ON hmap.profile_uuid = ds.profile_uuid AND hmap.host_uuid = ds.host_uuid
WHERE
- -- profile has been updated
- ( hmap.checksum != ds.checksum ) OR
+ -- profile has been updated
+ ( hmap.checksum != ds.checksum ) OR
-- profiles in A but not in B
- ( hmap.profile_id IS NULL AND hmap.host_uuid IS NULL ) OR
+ ( hmap.profile_uuid IS NULL AND hmap.host_uuid IS NULL ) OR
-- profiles in A and B but with operation type "remove"
( hmap.host_uuid IS NOT NULL AND ( hmap.operation_type = ? OR hmap.operation_type IS NULL ) ) OR
-- profiles in A and B with operation type "install" and NULL status
@@ -1579,26 +1621,26 @@ func (ds *Datastore) ListMDMAppleProfilesToRemove(ctx context.Context) ([]*fleet
// both, their desired state is necessarily to be installed).
query := `
SELECT
- hmap.profile_id,
- hmap.profile_identifier,
- hmap.profile_name,
- hmap.host_uuid,
- hmap.checksum,
- hmap.operation_type,
- COALESCE(hmap.detail, '') as detail,
- hmap.status,
- hmap.command_uuid
+ hmap.profile_uuid,
+ hmap.profile_identifier,
+ hmap.profile_name,
+ hmap.host_uuid,
+ hmap.checksum,
+ hmap.operation_type,
+ COALESCE(hmap.detail, '') as detail,
+ hmap.status,
+ hmap.command_uuid
FROM (
- SELECT h.uuid, macp.profile_id
+ SELECT h.uuid, macp.profile_uuid
FROM mdm_apple_configuration_profiles macp
JOIN hosts h ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0)
JOIN nano_enrollments ne ON ne.device_id = h.uuid
WHERE h.platform = 'darwin' AND ne.enabled = 1 AND ne.type = 'Device'
) as ds
RIGHT JOIN host_mdm_apple_profiles hmap
- ON hmap.profile_id = ds.profile_id AND hmap.host_uuid = ds.uuid
+ ON hmap.profile_uuid = ds.profile_uuid AND hmap.host_uuid = ds.uuid
-- profiles that are in B but not in A
- WHERE ds.profile_id IS NULL AND ds.uuid IS NULL
+ WHERE ds.profile_uuid IS NULL AND ds.uuid IS NULL
-- except "remove" operations in a terminal state or already pending
AND ( hmap.operation_type IS NULL OR hmap.operation_type != ? OR hmap.status IS NULL )
`
@@ -1608,16 +1650,16 @@ func (ds *Datastore) ListMDMAppleProfilesToRemove(ctx context.Context) ([]*fleet
return profiles, err
}
-func (ds *Datastore) GetMDMAppleProfilesContents(ctx context.Context, ids []uint) (map[uint]mobileconfig.Mobileconfig, error) {
- if len(ids) == 0 {
+func (ds *Datastore) GetMDMAppleProfilesContents(ctx context.Context, uuids []string) (map[string]mobileconfig.Mobileconfig, error) {
+ if len(uuids) == 0 {
return nil, nil
}
stmt := `
- SELECT profile_id, mobileconfig as mobileconfig
- FROM mdm_apple_configuration_profiles WHERE profile_id IN (?)
+ SELECT profile_uuid, mobileconfig as mobileconfig
+ FROM mdm_apple_configuration_profiles WHERE profile_uuid IN (?)
`
- query, args, err := sqlx.In(stmt, ids)
+ query, args, err := sqlx.In(stmt, uuids)
if err != nil {
return nil, err
}
@@ -1626,14 +1668,14 @@ func (ds *Datastore) GetMDMAppleProfilesContents(ctx context.Context, ids []uint
return nil, err
}
defer rows.Close()
- results := make(map[uint]mobileconfig.Mobileconfig)
+ results := make(map[string]mobileconfig.Mobileconfig)
for rows.Next() {
- var id uint
+ var uid string
var mobileconfig mobileconfig.Mobileconfig
- if err := rows.Scan(&id, &mobileconfig); err != nil {
+ if err := rows.Scan(&uid, &mobileconfig); err != nil {
return nil, err
}
- results[id] = mobileconfig
+ results[uid] = mobileconfig
}
return results, nil
}
@@ -1646,7 +1688,7 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
executeUpsertBatch := func(valuePart string, args []any) error {
stmt := fmt.Sprintf(`
INSERT INTO host_mdm_apple_profiles (
- profile_id,
+ profile_uuid,
profile_identifier,
profile_name,
host_uuid,
@@ -1654,10 +1696,10 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
operation_type,
detail,
command_uuid,
- checksum
+ checksum
)
VALUES %s
- ON DUPLICATE KEY UPDATE
+ ON DUPLICATE KEY UPDATE
status = VALUES(status),
operation_type = VALUES(operation_type),
detail = VALUES(detail),
@@ -1691,7 +1733,7 @@ func (ds *Datastore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
}
for _, p := range payload {
- args = append(args, p.ProfileID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.Detail, p.CommandUUID, p.Checksum)
+ args = append(args, p.ProfileUUID, p.ProfileIdentifier, p.ProfileName, p.HostUUID, p.Status, p.OperationType, p.Detail, p.CommandUUID, p.Checksum)
sb.WriteString("(?, ?, ?, ?, ?, ?, ?, ?, ?),")
batchCount++
@@ -2157,12 +2199,13 @@ func (ds *Datastore) BulkUpsertMDMAppleConfigProfiles(ctx context.Context, paylo
}
args = append(args, teamID, cp.Identifier, cp.Name, cp.Mobileconfig)
- sb.WriteString("(?, ?, ?, ?, UNHEX(MD5(mobileconfig))),")
+ // see https://stackoverflow.com/a/51393124/1094941
+ sb.WriteString("(CONCAT('a', CONVERT(uuid() USING utf8mb4)), ?, ?, ?, ?, UNHEX(MD5(mobileconfig))),")
}
stmt := fmt.Sprintf(`
INSERT INTO
- mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum)
+ mdm_apple_configuration_profiles (profile_uuid, team_id, identifier, name, mobileconfig, checksum)
VALUES %s
ON DUPLICATE KEY UPDATE
mobileconfig = VALUES(mobileconfig),
@@ -2396,6 +2439,7 @@ func getMDMAppleConfigProfileByTeamAndIdentifierDB(ctx context.Context, tx sqlx.
stmt := `
SELECT
+ profile_uuid,
profile_id,
team_id,
name,
diff --git a/server/datastore/mysql/apple_mdm_test.go b/server/datastore/mysql/apple_mdm_test.go
index 1c01141ba5..00250cf3f9 100644
--- a/server/datastore/mysql/apple_mdm_test.go
+++ b/server/datastore/mysql/apple_mdm_test.go
@@ -85,18 +85,25 @@ func testNewMDMAppleConfigProfileDuplicateName(t *testing.T, ds *Datastore) {
profA, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("a", "a", 0))
require.NoError(t, err)
require.NotZero(t, profA.ProfileID)
+ require.NotEmpty(t, profA.ProfileUUID)
+ require.Equal(t, "a", string(profA.ProfileUUID[0]))
profB, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("b", "b", 0))
require.NoError(t, err)
require.NotZero(t, profB.ProfileID)
+ require.NotEmpty(t, profB.ProfileUUID)
+ require.Equal(t, "a", string(profB.ProfileUUID[0]))
// create a Windows profile for no-team
profC, err := ds.NewMDMWindowsConfigProfile(ctx, fleet.MDMWindowsConfigProfile{Name: "c", TeamID: nil, SyncML: []byte("")})
require.NoError(t, err)
require.NotEmpty(t, profC.ProfileUUID)
+ require.Equal(t, "w", string(profC.ProfileUUID[0]))
// create the same name for team 1 as Apple profile
profATm, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("a", "a", 1))
require.NoError(t, err)
require.NotZero(t, profATm.ProfileID)
+ require.NotEmpty(t, profATm.ProfileUUID)
+ require.Equal(t, "a", string(profATm.ProfileUUID[0]))
require.NotNil(t, profATm.TeamID)
require.Equal(t, uint(1), *profATm.TeamID)
// create the same B profile for team 1 as Windows profile
@@ -151,7 +158,12 @@ func testNewMDMAppleConfigProfileDuplicateIdentifier(t *testing.T, ds *Datastore
newCP, err := ds.NewMDMAppleConfigProfile(ctx, duplicateCP)
require.NoError(t, err)
checkConfigProfile(t, duplicateCP, *newCP)
- storedCP, err := ds.GetMDMAppleConfigProfile(ctx, newCP.ProfileID)
+
+ // get it back from both the deprecated ID and the uuid methods
+ storedCP, err := ds.GetMDMAppleConfigProfileByDeprecatedID(ctx, newCP.ProfileID)
+ require.NoError(t, err)
+ checkConfigProfile(t, *newCP, *storedCP)
+ storedCP, err = ds.GetMDMAppleConfigProfile(ctx, newCP.ProfileUUID)
require.NoError(t, err)
checkConfigProfile(t, *newCP, *storedCP)
}
@@ -226,15 +238,25 @@ func testListMDMAppleConfigProfiles(t *testing.T, ds *Datastore) {
func testDeleteMDMAppleConfigProfile(t *testing.T, ds *Datastore) {
ctx := context.Background()
+
+ // first via the deprecated ID
initialCP := storeDummyConfigProfileForTest(t, ds)
-
- err := ds.DeleteMDMAppleConfigProfile(ctx, initialCP.ProfileID)
+ err := ds.DeleteMDMAppleConfigProfileByDeprecatedID(ctx, initialCP.ProfileID)
require.NoError(t, err)
-
- _, err = ds.GetMDMAppleConfigProfile(ctx, initialCP.ProfileID)
+ _, err = ds.GetMDMAppleConfigProfileByDeprecatedID(ctx, initialCP.ProfileID)
require.ErrorIs(t, err, sql.ErrNoRows)
- err = ds.DeleteMDMAppleConfigProfile(ctx, initialCP.ProfileID)
+ err = ds.DeleteMDMAppleConfigProfileByDeprecatedID(ctx, initialCP.ProfileID)
+ require.ErrorIs(t, err, sql.ErrNoRows)
+
+ // next via the uuid
+ initialCP = storeDummyConfigProfileForTest(t, ds)
+ err = ds.DeleteMDMAppleConfigProfile(ctx, initialCP.ProfileUUID)
+ require.NoError(t, err)
+ _, err = ds.GetMDMAppleConfigProfile(ctx, initialCP.ProfileUUID)
+ require.ErrorIs(t, err, sql.ErrNoRows)
+
+ err = ds.DeleteMDMAppleConfigProfile(ctx, initialCP.ProfileUUID)
require.ErrorIs(t, err, sql.ErrNoRows)
}
@@ -245,7 +267,7 @@ func testDeleteMDMAppleConfigProfileByTeamAndIdentifier(t *testing.T, ds *Datast
err := ds.DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx, initialCP.TeamID, initialCP.Identifier)
require.NoError(t, err)
- _, err = ds.GetMDMAppleConfigProfile(ctx, initialCP.ProfileID)
+ _, err = ds.GetMDMAppleConfigProfile(ctx, initialCP.ProfileUUID)
require.ErrorIs(t, err, sql.ErrNoRows)
err = ds.DeleteMDMAppleConfigProfileByTeamAndIdentifier(ctx, initialCP.TeamID, initialCP.Identifier)
@@ -266,7 +288,7 @@ func storeDummyConfigProfileForTest(t *testing.T, ds *Datastore) *fleet.MDMApple
newCP, err := ds.NewMDMAppleConfigProfile(ctx, dummyCP)
require.NoError(t, err)
checkConfigProfile(t, dummyCP, *newCP)
- storedCP, err := ds.GetMDMAppleConfigProfile(ctx, newCP.ProfileID)
+ storedCP, err := ds.GetMDMAppleConfigProfile(ctx, newCP.ProfileUUID)
require.NoError(t, err)
checkConfigProfile(t, *newCP, *storedCP)
@@ -338,30 +360,30 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Nil(t, gotProfs)
- expectedProfiles0 := map[uint]fleet.HostMDMAppleProfile{
- p0.ProfileID: {HostUUID: h0.UUID, Name: p0.Name, ProfileID: p0.ProfileID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
- p1.ProfileID: {HostUUID: h0.UUID, Name: p1.Name, ProfileID: p1.ProfileID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
- p2.ProfileID: {HostUUID: h0.UUID, Name: p2.Name, ProfileID: p2.ProfileID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
+ expectedProfiles0 := map[string]fleet.HostMDMAppleProfile{
+ p0.ProfileUUID: {HostUUID: h0.UUID, Name: p0.Name, ProfileUUID: p0.ProfileUUID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
+ p1.ProfileUUID: {HostUUID: h0.UUID, Name: p1.Name, ProfileUUID: p1.ProfileUUID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
+ p2.ProfileUUID: {HostUUID: h0.UUID, Name: p2.Name, ProfileUUID: p2.ProfileUUID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
}
- expectedProfiles1 := map[uint]fleet.HostMDMAppleProfile{
- p0.ProfileID: {HostUUID: h1.UUID, Name: p0.Name, ProfileID: p0.ProfileID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeInstall, Detail: "Error installing profile"},
- p1.ProfileID: {HostUUID: h1.UUID, Name: p1.Name, ProfileID: p1.ProfileID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
- p2.ProfileID: {HostUUID: h1.UUID, Name: p2.Name, ProfileID: p2.ProfileID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
+ expectedProfiles1 := map[string]fleet.HostMDMAppleProfile{
+ p0.ProfileUUID: {HostUUID: h1.UUID, Name: p0.Name, ProfileUUID: p0.ProfileUUID, CommandUUID: "cmd0-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeInstall, Detail: "Error installing profile"},
+ p1.ProfileUUID: {HostUUID: h1.UUID, Name: p1.Name, ProfileUUID: p1.ProfileUUID, CommandUUID: "cmd1-uuid", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
+ p2.ProfileUUID: {HostUUID: h1.UUID, Name: p2.Name, ProfileUUID: p2.ProfileUUID, CommandUUID: "cmd2-uuid", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
}
var args []interface{}
for _, p := range expectedProfiles0 {
- args = append(args, p.HostUUID, p.ProfileID, p.CommandUUID, *p.Status, p.OperationType, p.Detail, p.Name)
+ args = append(args, p.HostUUID, p.ProfileUUID, p.CommandUUID, *p.Status, p.OperationType, p.Detail, p.Name)
}
for _, p := range expectedProfiles1 {
- args = append(args, p.HostUUID, p.ProfileID, p.CommandUUID, *p.Status, p.OperationType, p.Detail, p.Name)
+ args = append(args, p.HostUUID, p.ProfileUUID, p.CommandUUID, *p.Status, p.OperationType, p.Detail, p.Name)
}
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
INSERT INTO host_mdm_apple_profiles (
- host_uuid, profile_id, command_uuid, status, operation_type, detail, profile_name)
+ host_uuid, profile_uuid, command_uuid, status, operation_type, detail, profile_name)
VALUES (?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?),(?,?,?,?,?,?,?)
`, args...,
)
@@ -379,7 +401,7 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Len(t, gotProfs, 3)
for _, gp := range gotProfs {
- ep, ok := expectedProfiles0[gp.ProfileID]
+ ep, ok := expectedProfiles0[gp.ProfileUUID]
require.True(t, ok)
require.Equal(t, ep.Name, gp.Name)
require.Equal(t, *ep.Status, *gp.Status)
@@ -395,7 +417,7 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Len(t, gotProfs, 3)
for _, gp := range gotProfs {
- ep, ok := expectedProfiles1[gp.ProfileID]
+ ep, ok := expectedProfiles1[gp.ProfileUUID]
require.True(t, ok)
require.Equal(t, ep.Name, gp.Name)
require.Equal(t, *ep.Status, *gp.Status)
@@ -404,11 +426,11 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
}
// mark h1's install+failed profile as install+pending
- h1InstallFailed := expectedProfiles1[p0.ProfileID]
+ h1InstallFailed := expectedProfiles1[p0.ProfileUUID]
err = ds.UpdateOrDeleteHostMDMAppleProfile(ctx, &fleet.HostMDMAppleProfile{
HostUUID: h1InstallFailed.HostUUID,
CommandUUID: h1InstallFailed.CommandUUID,
- ProfileID: h1InstallFailed.ProfileID,
+ ProfileUUID: h1InstallFailed.ProfileUUID,
Name: h1InstallFailed.Name,
Status: &fleet.MDMDeliveryPending,
OperationType: fleet.MDMOperationTypeInstall,
@@ -417,11 +439,11 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.NoError(t, err)
// mark h1's remove+failed profile as remove+verifying, deletes the host profile row
- h1RemoveFailed := expectedProfiles1[p2.ProfileID]
+ h1RemoveFailed := expectedProfiles1[p2.ProfileUUID]
err = ds.UpdateOrDeleteHostMDMAppleProfile(ctx, &fleet.HostMDMAppleProfile{
HostUUID: h1RemoveFailed.HostUUID,
CommandUUID: h1RemoveFailed.CommandUUID,
- ProfileID: h1RemoveFailed.ProfileID,
+ ProfileUUID: h1RemoveFailed.ProfileUUID,
Name: h1RemoveFailed.Name,
Status: &fleet.MDMDeliveryVerifying,
OperationType: fleet.MDMOperationTypeRemove,
@@ -436,10 +458,10 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
h1InstallPending := h1InstallFailed
h1InstallPending.Status = &fleet.MDMDeliveryPending
h1InstallPending.Detail = ""
- expectedProfiles1[p0.ProfileID] = h1InstallPending
- delete(expectedProfiles1, p2.ProfileID)
+ expectedProfiles1[p0.ProfileUUID] = h1InstallPending
+ delete(expectedProfiles1, p2.ProfileUUID)
for _, gp := range gotProfs {
- ep, ok := expectedProfiles1[gp.ProfileID]
+ ep, ok := expectedProfiles1[gp.ProfileUUID]
require.True(t, ok)
require.Equal(t, ep.Name, gp.Name)
require.Equal(t, *ep.Status, *gp.Status)
@@ -791,7 +813,7 @@ func testUpdateHostTablesOnMDMUnenroll(t *testing.T, ds *Datastore) {
err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: profiles[0].ProfileID,
+ ProfileUUID: profiles[0].ProfileUUID,
ProfileIdentifier: profiles[0].Identifier,
ProfileName: profiles[0].Name,
HostUUID: testUUID,
@@ -844,7 +866,7 @@ func expectAppleProfiles(
ds *Datastore,
tmID *uint,
want []*fleet.MDMAppleConfigProfile,
-) map[string]uint {
+) map[string]string {
if tmID == nil {
tmID = ptr.Uint(0)
}
@@ -857,14 +879,15 @@ func expectAppleProfiles(
})
// compare only the fields we care about, and build the resulting map of
- // profile identifier as key to profile ID as value
- m := make(map[string]uint)
+ // profile identifier as key to profile UUID as value
+ m := make(map[string]string)
for _, gotp := range got {
- m[gotp.Identifier] = gotp.ProfileID
+ m[gotp.Identifier] = gotp.ProfileUUID
if gotp.TeamID != nil && *gotp.TeamID == 0 {
gotp.TeamID = nil
}
gotp.ProfileID = 0
+ gotp.ProfileUUID = ""
gotp.CreatedAt = time.Time{}
gotp.UpdatedAt = time.Time{}
}
@@ -874,7 +897,7 @@ func expectAppleProfiles(
}
func testBatchSetMDMAppleProfiles(t *testing.T, ds *Datastore) {
- applyAndExpect := func(newSet []*fleet.MDMAppleConfigProfile, tmID *uint, want []*fleet.MDMAppleConfigProfile) map[string]uint {
+ applyAndExpect := func(newSet []*fleet.MDMAppleConfigProfile, tmID *uint, want []*fleet.MDMAppleConfigProfile) map[string]string {
ctx := context.Background()
err := ds.BatchSetMDMAppleProfiles(ctx, tmID, newSet)
require.NoError(t, err)
@@ -1112,9 +1135,9 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
- {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[0].ProfileUUID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[1].ProfileUUID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[2].ProfileUUID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
}, profiles)
// add another host, it belongs to a team
@@ -1135,9 +1158,9 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
- {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[0].ProfileUUID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[1].ProfileUUID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[2].ProfileUUID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
}, profiles)
// assign profiles to team 1
@@ -1159,11 +1182,11 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
- {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
- {ProfileID: teamPfs[0].ProfileID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-2"},
- {ProfileID: teamPfs[1].ProfileID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-2"},
+ {ProfileUUID: globalPfs[0].ProfileUUID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[1].ProfileUUID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[2].ProfileUUID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: teamPfs[0].ProfileUUID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-2"},
+ {ProfileUUID: teamPfs[1].ProfileUUID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-2"},
}, profiles)
// add another global host
@@ -1182,21 +1205,21 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
- {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
- {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
- {ProfileID: teamPfs[0].ProfileID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-2"},
- {ProfileID: teamPfs[1].ProfileID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-2"},
- {ProfileID: globalPfs[0].ProfileID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-3"},
- {ProfileID: globalPfs[1].ProfileID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-3"},
- {ProfileID: globalPfs[2].ProfileID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-3"},
+ {ProfileUUID: globalPfs[0].ProfileUUID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[1].ProfileUUID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: globalPfs[2].ProfileUUID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: teamPfs[0].ProfileUUID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-2"},
+ {ProfileUUID: teamPfs[1].ProfileUUID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-2"},
+ {ProfileUUID: globalPfs[0].ProfileUUID, ProfileIdentifier: globalPfs[0].Identifier, ProfileName: globalPfs[0].Name, HostUUID: "test-uuid-3"},
+ {ProfileUUID: globalPfs[1].ProfileUUID, ProfileIdentifier: globalPfs[1].Identifier, ProfileName: globalPfs[1].Name, HostUUID: "test-uuid-3"},
+ {ProfileUUID: globalPfs[2].ProfileUUID, ProfileIdentifier: globalPfs[2].Identifier, ProfileName: globalPfs[2].Name, HostUUID: "test-uuid-3"},
}, profiles)
// cron runs and updates the status
err = ds.BulkUpsertMDMAppleHostProfiles(
ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: globalPfs[0].ProfileID,
+ ProfileUUID: globalPfs[0].ProfileUUID,
ProfileIdentifier: globalPfs[0].Identifier,
ProfileName: globalPfs[0].Name,
Checksum: globalProfiles[0].Checksum,
@@ -1206,7 +1229,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[0].ProfileID,
+ ProfileUUID: globalPfs[0].ProfileUUID,
ProfileIdentifier: globalPfs[0].Identifier,
ProfileName: globalPfs[0].Name,
Checksum: globalProfiles[0].Checksum,
@@ -1216,7 +1239,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[1].ProfileID,
+ ProfileUUID: globalPfs[1].ProfileUUID,
ProfileIdentifier: globalPfs[1].Identifier,
ProfileName: globalPfs[1].Name,
Checksum: globalProfiles[1].Checksum,
@@ -1226,7 +1249,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[1].ProfileID,
+ ProfileUUID: globalPfs[1].ProfileUUID,
ProfileIdentifier: globalPfs[1].Identifier,
ProfileName: globalPfs[1].Name,
Checksum: globalProfiles[1].Checksum,
@@ -1236,7 +1259,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[2].ProfileID,
+ ProfileUUID: globalPfs[2].ProfileUUID,
ProfileIdentifier: globalPfs[2].Identifier,
ProfileName: globalPfs[2].Name,
Checksum: globalProfiles[2].Checksum,
@@ -1246,7 +1269,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[2].ProfileID,
+ ProfileUUID: globalPfs[2].ProfileUUID,
ProfileIdentifier: globalPfs[2].Identifier,
ProfileName: globalPfs[2].Name,
Checksum: globalProfiles[2].Checksum,
@@ -1256,7 +1279,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: teamPfs[0].ProfileID,
+ ProfileUUID: teamPfs[0].ProfileUUID,
ProfileIdentifier: teamPfs[0].Identifier,
ProfileName: teamPfs[0].Name,
Checksum: teamProfiles[0].Checksum,
@@ -1266,7 +1289,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: teamPfs[1].ProfileID,
+ ProfileUUID: teamPfs[1].ProfileUUID,
ProfileIdentifier: teamPfs[1].Identifier,
ProfileName: teamPfs[1].Name,
Checksum: teamProfiles[1].Checksum,
@@ -1316,8 +1339,8 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
profiles, err = ds.ListMDMAppleProfilesToInstall(ctx)
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
- {ProfileID: teamPfs[0].ProfileID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-1"},
- {ProfileID: teamPfs[1].ProfileID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: teamPfs[0].ProfileUUID, ProfileIdentifier: teamPfs[0].Identifier, ProfileName: teamPfs[0].Name, HostUUID: "test-uuid-1"},
+ {ProfileUUID: teamPfs[1].ProfileUUID, ProfileIdentifier: teamPfs[1].Identifier, ProfileName: teamPfs[1].Name, HostUUID: "test-uuid-1"},
}, profiles)
// profiles to be removed includes host1's old profiles
@@ -1325,7 +1348,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
require.NoError(t, err)
matchProfiles([]*fleet.MDMAppleProfilePayload{
{
- ProfileID: globalPfs[0].ProfileID,
+ ProfileUUID: globalPfs[0].ProfileUUID,
ProfileIdentifier: globalPfs[0].Identifier,
ProfileName: globalPfs[0].Name,
Status: &fleet.MDMDeliveryVerified,
@@ -1334,7 +1357,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[1].ProfileID,
+ ProfileUUID: globalPfs[1].ProfileUUID,
ProfileIdentifier: globalPfs[1].Identifier,
ProfileName: globalPfs[1].Name,
OperationType: fleet.MDMOperationTypeInstall,
@@ -1343,7 +1366,7 @@ func testMDMAppleProfileManagement(t *testing.T, ds *Datastore) {
CommandUUID: "command-uuid",
},
{
- ProfileID: globalPfs[2].ProfileID,
+ ProfileUUID: globalPfs[2].ProfileUUID,
ProfileIdentifier: globalPfs[2].Identifier,
ProfileName: globalPfs[2].Name,
OperationType: fleet.MDMOperationTypeInstall,
@@ -1404,24 +1427,24 @@ func testGetMDMAppleProfilesContents(t *testing.T, ds *Datastore) {
require.NoError(t, err)
cases := []struct {
- ids []uint
- want map[uint]mobileconfig.Mobileconfig
+ uuids []string
+ want map[string]mobileconfig.Mobileconfig
}{
- {[]uint{}, nil},
+ {[]string{}, nil},
{nil, nil},
- {[]uint{profiles[0].ProfileID}, map[uint]mobileconfig.Mobileconfig{profiles[0].ProfileID: profiles[0].Mobileconfig}},
+ {[]string{profiles[0].ProfileUUID}, map[string]mobileconfig.Mobileconfig{profiles[0].ProfileUUID: profiles[0].Mobileconfig}},
{
- []uint{profiles[0].ProfileID, profiles[1].ProfileID, profiles[2].ProfileID},
- map[uint]mobileconfig.Mobileconfig{
- profiles[0].ProfileID: profiles[0].Mobileconfig,
- profiles[1].ProfileID: profiles[1].Mobileconfig,
- profiles[2].ProfileID: profiles[2].Mobileconfig,
+ []string{profiles[0].ProfileUUID, profiles[1].ProfileUUID, profiles[2].ProfileUUID},
+ map[string]mobileconfig.Mobileconfig{
+ profiles[0].ProfileUUID: profiles[0].Mobileconfig,
+ profiles[1].ProfileUUID: profiles[1].Mobileconfig,
+ profiles[2].ProfileUUID: profiles[2].Mobileconfig,
},
},
}
for _, c := range cases {
- out, err := ds.GetMDMAppleProfilesContents(ctx, c.ids)
+ out, err := ds.GetMDMAppleProfilesContents(ctx, c.uuids)
require.NoError(t, err)
require.Equal(t, c.want, out)
}
@@ -1507,7 +1530,7 @@ func upsertHostCPs(
csum = cp.Checksum
}
payload := fleet.MDMAppleBulkUpsertHostProfilePayload{
- ProfileID: cp.ProfileID,
+ ProfileUUID: cp.ProfileUUID,
ProfileIdentifier: cp.Identifier,
ProfileName: cp.Name,
HostUUID: h.UUID,
@@ -2143,7 +2166,7 @@ func testIgnoreMDMClientError(t *testing.T, ds *Datastore) {
// create new record for remove pending
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{{
- ProfileID: uint(1),
+ ProfileUUID: "a" + uuid.NewString(),
ProfileIdentifier: "p1",
ProfileName: "name1",
HostUUID: "h1",
@@ -2174,7 +2197,7 @@ func testIgnoreMDMClientError(t *testing.T, ds *Datastore) {
// create another new record
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{{
- ProfileID: uint(2),
+ ProfileUUID: "a" + uuid.NewString(),
ProfileIdentifier: "p2",
ProfileName: "name2",
HostUUID: "h2",
@@ -2224,7 +2247,7 @@ func testDeleteMDMAppleProfilesForHost(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{{
- ProfileID: uint(1),
+ ProfileUUID: "a" + uuid.NewString(),
ProfileIdentifier: "p1",
ProfileName: "name1",
HostUUID: h.UUID,
@@ -3580,9 +3603,9 @@ func testSetVerifiedMacOSProfiles(t *testing.T, ds *Datastore) {
// simulate expired grace period by setting updated_at timestamp of profiles back by 24 hours
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
_, err := tx.ExecContext(ctx,
- `UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_id IN(?, ?, ?)`,
+ `UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_uuid IN(?, ?, ?)`,
time.Now().Add(-24*time.Hour),
- cp1.ProfileID, cp2.ProfileID, cp3.ProfileID,
+ cp1.ProfileUUID, cp2.ProfileUUID, cp3.ProfileUUID,
)
return err
})
@@ -4091,7 +4114,7 @@ func testMDMAppleConfigProfileHash(t *testing.T, ds *Datastore) {
require.NoError(t, err)
t.Cleanup(func() {
- err := ds.DeleteMDMAppleConfigProfile(ctx, prof.ProfileID)
+ err := ds.DeleteMDMAppleConfigProfile(ctx, prof.ProfileUUID)
require.NoError(t, err)
})
@@ -4099,11 +4122,11 @@ func testMDMAppleConfigProfileHash(t *testing.T, ds *Datastore) {
goHash := goProf.HexMD5Hash()
require.NotEmpty(t, goHash)
- var id uint
+ var uid string
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
- return sqlx.GetContext(ctx, q, &id, `SELECT profile_id FROM mdm_apple_configuration_profiles WHERE checksum = UNHEX(?)`, goHash)
+ return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM mdm_apple_configuration_profiles WHERE checksum = UNHEX(?)`, goHash)
})
- require.Equal(t, prof.ProfileID, id)
+ require.Equal(t, prof.ProfileUUID, uid)
})
}
}
@@ -4269,7 +4292,7 @@ func TestMDMAppleProfileVerification(t *testing.T) {
setProfileUpdatedAt := func(t *testing.T, cp *fleet.MDMAppleConfigProfile, ua time.Time) {
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
- _, err := tx.ExecContext(ctx, `UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_id = ?`, ua, cp.ProfileID)
+ _, err := tx.ExecContext(ctx, `UPDATE mdm_apple_configuration_profiles SET updated_at = ? WHERE profile_uuid = ?`, ua, cp.ProfileUUID)
return err
})
}
diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go
index b14b068b4d..36ebae62b4 100644
--- a/server/datastore/mysql/hosts_test.go
+++ b/server/datastore/mysql/hosts_test.go
@@ -759,9 +759,10 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) {
listHostsCheckCount(t, ds, userFilter, fleet.HostListOptions{TeamFilter: &team2.ID}, 4)
// test team filter in combination with macos settings filter
+ profUUID := "a" + uuid.NewString()
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: profUUID,
ProfileIdentifier: "identifier",
HostUUID: hosts[0].UUID, // hosts[0] is assgined to team 1
CommandUUID: "command-uuid-1",
@@ -779,7 +780,7 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) {
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: profUUID,
ProfileIdentifier: "identifier",
HostUUID: hosts[9].UUID, // hosts[9] is assgined to no team
CommandUUID: "command-uuid-2",
@@ -806,7 +807,7 @@ func testHostListOptionsTeamFilter(t *testing.T, ds *Datastore) {
// test team filter in combination with os settings disk encryptionfilter
require.NoError(t, ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: profUUID,
ProfileIdentifier: mobileconfig.FleetFileVaultPayloadIdentifier,
HostUUID: hosts[8].UUID, // hosts[8] is assgined to no team
CommandUUID: "command-uuid-3",
@@ -5834,7 +5835,7 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
prof, err := ds.NewMDMAppleConfigProfile(context.Background(), *configProfileForTest(t, "N1", "I1", "U1"))
require.NoError(t, err)
err = ds.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
- {ProfileID: prof.ProfileID, ProfileIdentifier: prof.Identifier, ProfileName: prof.Name, HostUUID: host.UUID, OperationType: fleet.MDMOperationTypeInstall, Checksum: []byte("csum")},
+ {ProfileUUID: prof.ProfileUUID, ProfileIdentifier: prof.Identifier, ProfileName: prof.Name, HostUUID: host.UUID, OperationType: fleet.MDMOperationTypeInstall, Checksum: []byte("csum")},
})
require.NoError(t, err)
diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go
index 49c5d7fa92..76dcb5eb54 100644
--- a/server/datastore/mysql/labels_test.go
+++ b/server/datastore/mysql/labels_test.go
@@ -13,6 +13,7 @@ import (
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/fleetdm/fleet/v4/server/test"
"github.com/google/go-cmp/cmp"
+ "github.com/google/uuid"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -490,7 +491,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da
// test team filter in combination with macos settings filter
require.NoError(t, db.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: "a" + uuid.NewString(),
ProfileIdentifier: "identifier",
HostUUID: h1.UUID, // hosts[0] is assgined to team 1
CommandUUID: "command-uuid-1",
@@ -508,7 +509,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da
require.NoError(t, db.BulkUpsertMDMAppleHostProfiles(context.Background(), []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: "a" + uuid.NewString(),
ProfileIdentifier: "identifier",
HostUUID: h2.UUID, // hosts[9] is assgined to no team
CommandUUID: "command-uuid-2",
diff --git a/server/datastore/mysql/mdm.go b/server/datastore/mysql/mdm.go
index 46b0aa7c11..e0f0ab5e63 100644
--- a/server/datastore/mysql/mdm.go
+++ b/server/datastore/mysql/mdm.go
@@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
+ "strings"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
@@ -108,7 +109,7 @@ func (ds *Datastore) ListMDMConfigProfiles(ctx context.Context, teamID *uint, op
const selectStmt = `
SELECT
- profile_id,
+ profile_uuid,
team_id,
name,
platform,
@@ -118,7 +119,7 @@ SELECT
updated_at
FROM (
SELECT
- CONVERT(profile_id, CHAR) as profile_id,
+ profile_uuid,
team_id,
name,
'darwin' as platform,
@@ -135,7 +136,7 @@ FROM (
UNION
SELECT
- profile_uuid as profile_id,
+ profile_uuid,
team_id,
name,
'windows' as platform,
@@ -195,31 +196,45 @@ FROM (
// slice arguments can have values.
func (ds *Datastore) BulkSetPendingMDMHostProfiles(
ctx context.Context,
- hostIDs, teamIDs, profileIDs []uint,
+ hostIDs, teamIDs []uint,
profileUUIDs, hostUUIDs []string,
) error {
- var countArgs int
+ var (
+ countArgs int
+ macProfUUIDs []string
+ winProfUUIDs []string
+ )
+
if len(hostIDs) > 0 {
countArgs++
}
if len(teamIDs) > 0 {
countArgs++
}
- if len(profileIDs) > 0 {
- countArgs++
- }
if len(profileUUIDs) > 0 {
countArgs++
+
+ // split into mac and win profiles
+ for _, puid := range profileUUIDs {
+ if strings.HasPrefix(puid, "a") {
+ macProfUUIDs = append(macProfUUIDs, puid)
+ } else {
+ winProfUUIDs = append(winProfUUIDs, puid)
+ }
+ }
}
if len(hostUUIDs) > 0 {
countArgs++
}
if countArgs > 1 {
- return errors.New("only one of hostIDs, teamIDs, profileIDs, profileUUIDs or hostUUIDs can be provided")
+ return errors.New("only one of hostIDs, teamIDs, profileUUIDs or hostUUIDs can be provided")
}
if countArgs == 0 {
return nil
}
+ if len(macProfUUIDs) > 0 && len(winProfUUIDs) > 0 {
+ return errors.New("profile uuids must all be Apple or Windows profiles")
+ }
var (
hosts []fleet.Host
@@ -257,8 +272,8 @@ func (ds *Datastore) BulkSetPendingMDMHostProfiles(
}
}
- case len(profileIDs) > 0:
- // TODO: if a very large number (~65K) of profile IDs was provided, could
+ case len(macProfUUIDs) > 0:
+ // TODO: if a very large number (~65K) of profile UUIDs was provided, could
// result in too many placeholders (not an immediate concern).
uuidStmt = `
SELECT DISTINCT h.uuid, h.platform
@@ -266,10 +281,10 @@ FROM hosts h
JOIN mdm_apple_configuration_profiles macp
ON h.team_id = macp.team_id OR (h.team_id IS NULL AND macp.team_id = 0)
WHERE
- macp.profile_id IN (?) AND h.platform = 'darwin'`
- args = append(args, profileIDs)
+ macp.profile_uuid IN (?) AND h.platform = 'darwin'`
+ args = append(args, macProfUUIDs)
- case len(profileUUIDs) > 0:
+ case len(winProfUUIDs) > 0:
// TODO: if a very large number (~65K) of profile IDs was provided, could
// result in too many placeholders (not an immediate concern).
uuidStmt = `
@@ -279,7 +294,7 @@ JOIN mdm_windows_configuration_profiles mawp
ON h.team_id = mawp.team_id OR (h.team_id IS NULL AND mawp.team_id = 0)
WHERE
mawp.profile_uuid IN (?) AND h.platform = 'windows'`
- args = append(args, profileUUIDs)
+ args = append(args, winProfUUIDs)
}
diff --git a/server/datastore/mysql/mdm_test.go b/server/datastore/mysql/mdm_test.go
index 6eb7160319..cec25073af 100644
--- a/server/datastore/mysql/mdm_test.go
+++ b/server/datastore/mysql/mdm_test.go
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"sort"
- "strconv"
"testing"
"github.com/fleetdm/fleet/v4/server/fleet"
@@ -468,7 +467,7 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
}
type anyProfile struct {
- ProfileID string `db:"profile_uuid"`
+ ProfileUUID string `db:"profile_uuid"`
Status *fleet.MDMDeliveryStatus `db:"status"`
OperationType fleet.MDMOperationType `db:"operation_type"`
IdentifierOrName string `db:"profile_name"`
@@ -499,7 +498,7 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Equal(t, len(wantProfs), len(profs), "host uuid: %s", h.UUID)
for _, p := range profs {
gotProfs = append(gotProfs, anyProfile{
- ProfileID: fmt.Sprint(p.ProfileID),
+ ProfileUUID: p.ProfileUUID,
Status: p.Status,
OperationType: p.OperationType,
IdentifierOrName: p.Identifier,
@@ -510,7 +509,7 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
sortProfs := func(profs []anyProfile) []anyProfile {
sort.Slice(profs, func(i, j int) bool {
l, r := profs[i], profs[j]
- if l.ProfileID == r.ProfileID {
+ if l.ProfileUUID == r.ProfileUUID {
return l.OperationType < r.OperationType
}
@@ -524,7 +523,7 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
wantProfs = sortProfs(wantProfs)
for i, wp := range wantProfs {
gp := gotProfs[i]
- require.Equal(t, wp.ProfileID, gp.ProfileID, "host uuid: %s, prof id or name: %s", h.UUID, gp.IdentifierOrName)
+ require.Equal(t, wp.ProfileUUID, gp.ProfileUUID, "host uuid: %s, prof id or name: %s", h.UUID, gp.IdentifierOrName)
require.Equal(t, wp.Status, gp.Status, "host uuid: %s, prof id or name: %s", h.UUID, gp.IdentifierOrName)
require.Equal(t, wp.OperationType, gp.OperationType, "host uuid: %s, prof id or name: %s", h.UUID, gp.IdentifierOrName)
}
@@ -603,16 +602,16 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
}
// bulk set for no target ids, does nothing
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, nil)
require.NoError(t, err)
// bulk set for combination of target ids, not allowed
- err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{1}, []uint{2}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, []uint{1}, []uint{2}, nil, nil)
require.Error(t, err)
// bulk set for all created hosts, no profiles yet so nothing changed
allHosts := append(darwinHosts, unenrolledHost, linuxHost)
allHosts = append(allHosts, windowsHosts...)
- err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {},
@@ -662,40 +661,40 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, toRemoveWindows, 0)
// bulk set for all created hosts, enrolled hosts get the no-team profiles
- err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(allHosts...), nil, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -724,37 +723,37 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, toRemoveWindows, 3)
// update status of the moved host (team has no profiles)
- err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(darwinHosts[0], windowsHosts[0]), nil, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(darwinHosts[0], windowsHosts[0]), nil, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
// windows profiles are directly deleted without a pending state
windowsHosts[0]: {},
windowsHosts[1]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -784,23 +783,23 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, toRemoveWindows, 3)
// update status of the moved host via its uuid (team has no profiles)
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, nil, []string{darwinHosts[1].UUID, windowsHosts[1].UUID})
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, []string{darwinHosts[1].UUID, windowsHosts[1].UUID})
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
@@ -808,9 +807,9 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
// windows profiles are directly deleted without a pending state
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -847,37 +846,37 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, toRemoveWindows, 0)
// update status of the affected team
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: tm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: tm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: tm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: tm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: tm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: tm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: tm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: tm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -894,15 +893,15 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
// all rows in this test since we don't have command uuids.
err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- HostUUID: darwinHosts[0].UUID, ProfileID: darwinGlobalProfiles[0].ProfileID,
+ HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[0].ProfileUUID,
Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"),
},
{
- HostUUID: darwinHosts[0].UUID, ProfileID: darwinGlobalProfiles[1].ProfileID,
+ HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[1].ProfileUUID,
Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"),
},
{
- HostUUID: darwinHosts[0].UUID, ProfileID: darwinGlobalProfiles[2].ProfileID,
+ HostUUID: darwinHosts[0].UUID, ProfileUUID: darwinGlobalProfiles[2].ProfileUUID,
Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Checksum: []byte("csum"),
},
})
@@ -925,37 +924,37 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, newTm1Profiles, 4)
// update status of the affected team
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: tm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: tm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -980,38 +979,38 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, newTm1Profiles, 6)
// update status of the affected team
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team1.ID}, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: globalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: globalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -1035,39 +1034,39 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, newGlobalProfiles, 6)
// update status of the affected "no-team"
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{0}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{0}, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newGlobalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -1092,80 +1091,80 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
newGlobalProfiles = getProfs(nil)
require.Len(t, newGlobalProfiles, 8)
- newDarwinProfileID, err := strconv.ParseUint(newGlobalProfiles[3].ProfileID, 10, 64)
+ newDarwinProfileUUID := newGlobalProfiles[3].ProfileUUID
require.NoError(t, err)
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []uint{uint(newDarwinProfileID)}, []string{}, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newDarwinProfileUUID}, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newGlobalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: newGlobalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[6].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[6].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []uint{}, []string{newGlobalProfiles[7].ProfileID}, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newGlobalProfiles[7].ProfileUUID}, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newGlobalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {},
windowsHosts[2]: {
- {ProfileID: newGlobalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[6].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[7].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[6].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[7].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
@@ -1185,50 +1184,50 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
require.Len(t, tm2Profiles, 2)
// update status via tm2 id and the global 0 id to test that custom sql statement
- err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID, 0}, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{team2.ID, 0}, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: tm2Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: tm2Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newGlobalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {
- {ProfileID: tm2Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: tm2Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[2]: {
- {ProfileID: newGlobalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[6].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[7].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[6].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[7].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
// simulate an entry with some values set to NULL
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
- _, err := q.ExecContext(ctx, `UPDATE host_mdm_apple_profiles SET detail = NULL WHERE profile_id = ?`, globalProfiles[2].ProfileID)
+ _, err := q.ExecContext(ctx, `UPDATE host_mdm_apple_profiles SET detail = NULL WHERE profile_uuid = ?`, globalProfiles[2].ProfileUUID)
if err != nil {
return err
}
@@ -1236,44 +1235,44 @@ func testBulkSetPendingMDMHostProfiles(t *testing.T, ds *Datastore) {
})
// do a final sync of all hosts, should not change anything
- err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(append(darwinHosts, unenrolledHost, linuxHost)...), nil, nil, nil, nil)
+ err = ds.BulkSetPendingMDMHostProfiles(ctx, hostIDsFromHosts(append(darwinHosts, unenrolledHost, linuxHost)...), nil, nil, nil)
require.NoError(t, err)
assertHostProfiles(map[*fleet.Host][]anyProfile{
darwinHosts[0]: {
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newTm1Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newTm1Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[1]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: globalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: tm2Profiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: globalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: tm2Profiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
darwinHosts[2]: {
- {ProfileID: globalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
- {ProfileID: newGlobalProfiles[0].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[2].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: globalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeRemove},
+ {ProfileUUID: newGlobalProfiles[0].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[2].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
unenrolledHost: {},
linuxHost: {},
windowsHosts[0]: {
- {ProfileID: newTm1Profiles[3].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newTm1Profiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[3].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newTm1Profiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[1]: {
- {ProfileID: tm2Profiles[1].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: tm2Profiles[1].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
windowsHosts[2]: {
- {ProfileID: newGlobalProfiles[4].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[5].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[6].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
- {ProfileID: newGlobalProfiles[7].ProfileID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[4].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[5].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[6].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
+ {ProfileUUID: newGlobalProfiles[7].ProfileUUID, Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall},
},
})
}
diff --git a/server/datastore/mysql/microsoft_mdm.go b/server/datastore/mysql/microsoft_mdm.go
index 630c6cf0de..ee51a5d13a 100644
--- a/server/datastore/mysql/microsoft_mdm.go
+++ b/server/datastore/mysql/microsoft_mdm.go
@@ -1341,7 +1341,7 @@ func (ds *Datastore) bulkDeleteMDMWindowsHostsConfigProfilesDB(
}
func (ds *Datastore) NewMDMWindowsConfigProfile(ctx context.Context, cp fleet.MDMWindowsConfigProfile) (*fleet.MDMWindowsConfigProfile, error) {
- profileUUID := uuid.New().String()
+ profileUUID := "w" + uuid.New().String()
stmt := `
INSERT INTO
mdm_windows_configuration_profiles (profile_uuid, team_id, name, syncml)
@@ -1470,7 +1470,8 @@ INSERT INTO
profile_uuid, team_id, name, syncml
)
VALUES
- ( UUID(), ?, ?, ? )
+ -- see https://stackoverflow.com/a/51393124/1094941
+ ( CONCAT('w', CONVERT(UUID() USING utf8mb4)), ?, ?, ? )
ON DUPLICATE KEY UPDATE
name = VALUES(name),
syncml = VALUES(syncml)
diff --git a/server/datastore/mysql/microsoft_mdm_test.go b/server/datastore/mysql/microsoft_mdm_test.go
index 3aceb49b0f..30831d0829 100644
--- a/server/datastore/mysql/microsoft_mdm_test.go
+++ b/server/datastore/mysql/microsoft_mdm_test.go
@@ -526,7 +526,7 @@ func testMDMWindowsDiskEncryption(t *testing.T, ds *Datastore) {
HostUUID: hosts[5].UUID,
ProfileIdentifier: mobileconfig.FleetFileVaultPayloadIdentifier,
ProfileName: "Disk encryption",
- ProfileID: 1,
+ ProfileUUID: "a" + uuid.NewString(),
CommandUUID: uuid.New().String(),
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryFailed,
@@ -1741,6 +1741,7 @@ func testMDMWindowsConfigProfiles(t *testing.T, ds *Datastore) {
profC, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("c", "c", 0))
require.NoError(t, err)
require.NotZero(t, profC.ProfileID)
+ require.NotEmpty(t, profC.ProfileUUID)
// create the same name for team 1 as Windows profile
profATm, err := ds.NewMDMWindowsConfigProfile(ctx, fleet.MDMWindowsConfigProfile{Name: "a", TeamID: ptr.Uint(1), SyncML: []byte("")})
@@ -1752,6 +1753,7 @@ func testMDMWindowsConfigProfiles(t *testing.T, ds *Datastore) {
profBTm, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("b", "b", 1))
require.NoError(t, err)
require.NotZero(t, profBTm.ProfileID)
+ require.NotEmpty(t, profBTm.ProfileUUID)
var existsErr *existsError
// create a duplicate of Windows for no-team
diff --git a/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID.go b/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID.go
new file mode 100644
index 0000000000..6a317f9768
--- /dev/null
+++ b/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID.go
@@ -0,0 +1,130 @@
+package tables
+
+import (
+ "database/sql"
+ "fmt"
+)
+
+func init() {
+ MigrationClient.AddMigration(Up_20231204155427, Down_20231204155427)
+}
+
+func Up_20231204155427(tx *sql.Tx) error {
+ // update the windows profiles tables to use a 37-char uuid column for
+ // the 'w' prefix.
+ _, err := tx.Exec(`
+ALTER TABLE host_mdm_windows_profiles
+ CHANGE COLUMN profile_uuid profile_uuid VARCHAR(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''
+`)
+ if err != nil {
+ return fmt.Errorf("failed to alter host_mdm_windows_profiles table: %w", err)
+ }
+ _, err = tx.Exec(`
+ALTER TABLE mdm_windows_configuration_profiles
+ CHANGE COLUMN profile_uuid profile_uuid VARCHAR(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''
+`)
+ if err != nil {
+ return fmt.Errorf("failed to alter mdm_windows_configuration_profiles table: %w", err)
+ }
+
+ // add the 'w' prefix to the windows profiles table
+ _, err = tx.Exec(`
+UPDATE
+ mdm_windows_configuration_profiles
+SET
+ profile_uuid = CONCAT('w', profile_uuid)
+`)
+ if err != nil {
+ return fmt.Errorf("failed to update mdm_windows_configuration_profiles table: %w", err)
+ }
+ _, err = tx.Exec(`
+UPDATE
+ host_mdm_windows_profiles
+SET
+ profile_uuid = CONCAT('w', profile_uuid)
+`)
+ if err != nil {
+ return fmt.Errorf("failed to update host_mdm_windows_profiles table: %w", err)
+ }
+
+ // update the apple profiles table to add the profile_uuid column and
+ // temporarily drop the primary key until we fill those uuids.
+ _, err = tx.Exec(`
+ALTER TABLE mdm_apple_configuration_profiles
+ -- 37 and not 36 because the UUID will be prefixed with 'a' to indicate
+ -- that it's an Apple profile.
+ ADD COLUMN profile_uuid VARCHAR(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
+ -- auto-increment column must have an index, so we create one before
+ -- dropping the primary key to make it profile_uuid later.
+ ADD UNIQUE KEY idx_mdm_apple_config_prof_id (profile_id),
+ DROP PRIMARY KEY
+`)
+ if err != nil {
+ return fmt.Errorf("failed to alter mdm_apple_configuration_profiles table: %w", err)
+ }
+
+ // generate the uuids for the apple profiles table
+ _, err = tx.Exec(`
+UPDATE
+ mdm_apple_configuration_profiles
+SET
+ -- see https://stackoverflow.com/a/51393124/1094941
+ profile_uuid = CONCAT('a', CONVERT(uuid() USING utf8mb4))
+`)
+ if err != nil {
+ return fmt.Errorf("failed to update mdm_apple_configuration_profiles table: %w", err)
+ }
+
+ // set the profile uuid as the new primary key
+ _, err = tx.Exec(`
+ALTER TABLE mdm_apple_configuration_profiles
+ ADD PRIMARY KEY (profile_uuid)`)
+ if err != nil {
+ return fmt.Errorf("failed to set primary key of mdm_apple_configuration_profiles table: %w", err)
+ }
+
+ // add the profile_uuid column to the host apple profiles table, keeping the
+ // old id for now. Cannot be set as primary key yet as it may have duplicates
+ // until we generate the uuids.
+ _, err = tx.Exec(`
+ALTER TABLE host_mdm_apple_profiles
+ DROP PRIMARY KEY,
+ ADD COLUMN profile_uuid VARCHAR(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''
+`)
+ if err != nil {
+ return fmt.Errorf("failed to alter host_mdm_apple_profiles table: %w", err)
+ }
+
+ // update the apple host profiles table's profile_uuid based on its profile_id
+ _, err = tx.Exec(`
+UPDATE
+ host_mdm_apple_profiles
+SET
+ profile_uuid = COALESCE((
+ SELECT
+ macp.profile_uuid
+ FROM
+ mdm_apple_configuration_profiles macp
+ WHERE
+ host_mdm_apple_profiles.profile_id = macp.profile_id
+ -- see https://stackoverflow.com/a/51393124/1094941
+ ), CONCAT('a', CONVERT(uuid() USING utf8mb4)))
+`)
+ if err != nil {
+ return fmt.Errorf("failed to update host_mdm_apple_profiles table: %w", err)
+ }
+
+ // drop the now unused profile_id column from the host apple profiles table
+ _, err = tx.Exec(`ALTER TABLE host_mdm_apple_profiles
+ ADD PRIMARY KEY (host_uuid, profile_uuid),
+ DROP COLUMN profile_id`)
+ if err != nil {
+ return fmt.Errorf("failed to drop column from host_mdm_apple_profiles table: %w", err)
+ }
+
+ return nil
+}
+
+func Down_20231204155427(tx *sql.Tx) error {
+ return nil
+}
diff --git a/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID_test.go b/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID_test.go
new file mode 100644
index 0000000000..23a3772f75
--- /dev/null
+++ b/server/datastore/mysql/migrations/tables/20231204155427_AlterMacOSProfilesPrimaryKeyToUUID_test.go
@@ -0,0 +1,142 @@
+package tables
+
+import (
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/jmoiron/sqlx"
+ "github.com/stretchr/testify/require"
+)
+
+func TestUp_20231204155427(t *testing.T) {
+ db := applyUpToPrev(t)
+
+ // create some Windows profiles
+ idwA, idwB, idwC := uuid.New().String(), uuid.New().String(), uuid.New().String()
+ execNoErr(t, db, `INSERT INTO mdm_windows_configuration_profiles (profile_uuid, team_id, name, syncml) VALUES (?, 0, 'A', 'A')`, idwA)
+ execNoErr(t, db, `INSERT INTO mdm_windows_configuration_profiles (profile_uuid, team_id, name, syncml) VALUES (?, 1, 'B', 'B')`, idwB)
+ execNoErr(t, db, `INSERT INTO mdm_windows_configuration_profiles (profile_uuid, team_id, name, syncml) VALUES (?, 0, 'C', 'C')`, idwC)
+ nonExistingWID := uuid.New().String()
+
+ // create some Windows hosts profiles with one not related to an existing profile
+ execNoErr(t, db, `INSERT INTO host_mdm_windows_profiles (host_uuid, profile_uuid, command_uuid) VALUES ('h1', ?, 'c1')`, idwA)
+ execNoErr(t, db, `INSERT INTO host_mdm_windows_profiles (host_uuid, profile_uuid, command_uuid) VALUES ('h2', ?, 'c2')`, idwB)
+ execNoErr(t, db, `INSERT INTO host_mdm_windows_profiles (host_uuid, profile_uuid, command_uuid) VALUES ('h2', ?, 'c3')`, nonExistingWID)
+ execNoErr(t, db, `INSERT INTO host_mdm_windows_profiles (host_uuid, profile_uuid, command_uuid) VALUES ('h2', ?, 'c4')`, idwA)
+
+ // create some Apple profiles
+ idaA := execNoErrLastID(t, db, `INSERT INTO mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum) VALUES (0, 'IA', 'NA', '', '')`)
+ idaB := execNoErrLastID(t, db, `INSERT INTO mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum) VALUES (1, 'IB', 'NB', '', '')`)
+ idaC := execNoErrLastID(t, db, `INSERT INTO mdm_apple_configuration_profiles (team_id, identifier, name, mobileconfig, checksum) VALUES (0, 'IC', 'NC', '', '')`)
+ nonExistingAID := idaC + 1000
+
+ // create some Apple hosts profiles with one not related to an existing profile
+ execNoErr(t, db, `INSERT INTO host_mdm_apple_profiles (host_uuid, profile_id, command_uuid, profile_identifier, checksum) VALUES ('h1', ?, 'c1', 'IA', '')`, idaA)
+ execNoErr(t, db, `INSERT INTO host_mdm_apple_profiles (host_uuid, profile_id, command_uuid, profile_identifier, checksum) VALUES ('h2', ?, 'c2', 'IB', '')`, idaB)
+ execNoErr(t, db, `INSERT INTO host_mdm_apple_profiles (host_uuid, profile_id, command_uuid, profile_identifier, checksum) VALUES ('h2', ?, 'c3', 'IZ', '')`, nonExistingAID)
+ execNoErr(t, db, `INSERT INTO host_mdm_apple_profiles (host_uuid, profile_id, command_uuid, profile_identifier, checksum) VALUES ('h2', ?, 'c4', 'IA', '')`, idaA)
+
+ // Apply current migration.
+ applyNext(t, db)
+
+ // Windows profile uuids were updated with the prefix
+ var wprofUUIDs []string
+ err := sqlx.Select(db, &wprofUUIDs, `SELECT profile_uuid FROM mdm_windows_configuration_profiles ORDER BY name`)
+ require.NoError(t, err)
+ require.Len(t, wprofUUIDs, 3)
+ require.Equal(t, "w"+idwA, wprofUUIDs[0])
+ require.Equal(t, "w"+idwB, wprofUUIDs[1])
+ require.Equal(t, "w"+idwC, wprofUUIDs[2])
+
+ // Apple profiles were assigned uuids in addition to identifier
+ var aprofUUIDs []string
+ err = sqlx.Select(db, &aprofUUIDs, `SELECT profile_uuid FROM mdm_apple_configuration_profiles ORDER BY name`)
+ require.NoError(t, err)
+ require.Len(t, aprofUUIDs, 3)
+ require.Len(t, aprofUUIDs[0], 37)
+ require.Len(t, aprofUUIDs[1], 37)
+ require.Len(t, aprofUUIDs[2], 37)
+ require.Equal(t, "a", string(aprofUUIDs[0][0]))
+ require.Equal(t, "a", string(aprofUUIDs[1][0]))
+ require.Equal(t, "a", string(aprofUUIDs[2][0]))
+
+ var hostUUIDs []string
+ // get Windows hosts with profile A
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, wprofUUIDs[0])
+ require.NoError(t, err)
+ require.Equal(t, []string{"h1", "h2"}, hostUUIDs)
+
+ // get Windows hosts with profile B
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, wprofUUIDs[1])
+ require.NoError(t, err)
+ require.Equal(t, []string{"h2"}, hostUUIDs)
+
+ // get Windows hosts with profile C
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, wprofUUIDs[2])
+ require.NoError(t, err)
+ require.Empty(t, hostUUIDs)
+
+ // get Windows hosts with unknown profile
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_windows_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, "w"+nonExistingWID)
+ require.NoError(t, err)
+ require.Equal(t, []string{"h2"}, hostUUIDs)
+
+ // get profile uuid of non-existing profile
+ var nonExistingProfUUIDs []string
+ err = sqlx.Select(db, &nonExistingProfUUIDs, `SELECT profile_uuid FROM host_mdm_windows_profiles WHERE command_uuid = 'c3' ORDER BY profile_uuid`)
+ require.NoError(t, err)
+ require.Len(t, nonExistingProfUUIDs, 1)
+ require.Len(t, nonExistingProfUUIDs[0], 37)
+ require.Equal(t, "w", string(nonExistingProfUUIDs[0][0]))
+
+ // get Apple hosts with profile NA
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_apple_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, aprofUUIDs[0])
+ require.NoError(t, err)
+ require.Equal(t, []string{"h1", "h2"}, hostUUIDs)
+
+ // get Apple hosts with profile NB
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_apple_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, aprofUUIDs[1])
+ require.NoError(t, err)
+ require.Equal(t, []string{"h2"}, hostUUIDs)
+
+ // get Apple hosts with profile C
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_apple_profiles WHERE profile_uuid = ? ORDER BY host_uuid`, aprofUUIDs[2])
+ require.NoError(t, err)
+ require.Empty(t, hostUUIDs)
+
+ // get Apple hosts with unknown profile, it was assigned an apple uuid
+ hostUUIDs = hostUUIDs[:0]
+ err = sqlx.Select(db, &hostUUIDs, `SELECT host_uuid FROM host_mdm_apple_profiles WHERE profile_identifier = 'IZ' ORDER BY host_uuid`)
+ require.NoError(t, err)
+ require.Equal(t, []string{"h2"}, hostUUIDs)
+ nonExistingProfUUIDs = nonExistingProfUUIDs[:0]
+ err = sqlx.Select(db, &nonExistingProfUUIDs, `SELECT profile_uuid FROM host_mdm_apple_profiles WHERE profile_identifier = 'IZ' ORDER BY host_uuid`)
+ require.NoError(t, err)
+ require.Len(t, nonExistingProfUUIDs, 1)
+ require.Len(t, nonExistingProfUUIDs[0], 37)
+ require.Equal(t, "a", string(nonExistingProfUUIDs[0][0]))
+
+ // creating a new Apple profile still generates a unique numerical id
+ idaD := execNoErrLastID(t, db, `INSERT INTO mdm_apple_configuration_profiles (profile_uuid, team_id, identifier, name, mobileconfig, checksum) VALUES (CONCAT('a', CONVERT(uuid() USING utf8mb4)), 0, 'ID', 'ND', '', '')`)
+ require.NotZero(t, idaD)
+ require.Greater(t, idaD, idaC)
+
+ // batch-creating new Apple profiles also generates unique numerical ids
+ execNoErr(t, db, `INSERT INTO mdm_apple_configuration_profiles
+ (profile_uuid, team_id, identifier, name, mobileconfig, checksum)
+ VALUES
+ (CONCAT('a', CONVERT(uuid() USING utf8mb4)), 0, 'IE', 'NE', '', ''),
+ (CONCAT('a', CONVERT(uuid() USING utf8mb4)), 0, 'IF', 'NF', '', ''),
+ (CONCAT('a', CONVERT(uuid() USING utf8mb4)), 0, 'IG', 'NG', '', '')
+`)
+ var profIDs []int64
+ err = sqlx.Select(db, &profIDs, `SELECT profile_id FROM mdm_apple_configuration_profiles ORDER BY name`)
+ require.NoError(t, err)
+ require.Equal(t, []int64{idaA, idaB, idaC, idaD, idaD + 1, idaD + 2, idaD + 3}, profIDs)
+}
diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql
index 91b582a954..0cd2d06d1a 100644
--- a/server/datastore/mysql/schema.sql
+++ b/server/datastore/mysql/schema.sql
@@ -277,7 +277,6 @@ CREATE TABLE `host_mdm_apple_bootstrap_packages` (
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `host_mdm_apple_profiles` (
- `profile_id` int(10) unsigned NOT NULL,
`profile_identifier` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`host_uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
@@ -287,7 +286,8 @@ CREATE TABLE `host_mdm_apple_profiles` (
`profile_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`checksum` binary(16) NOT NULL,
`retries` tinyint(3) unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`host_uuid`,`profile_id`),
+ `profile_uuid` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
+ PRIMARY KEY (`host_uuid`,`profile_uuid`),
KEY `status` (`status`),
KEY `operation_type` (`operation_type`),
CONSTRAINT `host_mdm_apple_profiles_ibfk_1` FOREIGN KEY (`status`) REFERENCES `mdm_delivery_status` (`status`) ON UPDATE CASCADE,
@@ -304,7 +304,7 @@ CREATE TABLE `host_mdm_windows_profiles` (
`command_uuid` varchar(127) COLLATE utf8mb4_unicode_ci NOT NULL,
`profile_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`retries` tinyint(3) unsigned NOT NULL DEFAULT '0',
- `profile_uuid` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
+ `profile_uuid` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`host_uuid`,`profile_uuid`),
KEY `status` (`status`),
KEY `operation_type` (`operation_type`),
@@ -587,9 +587,11 @@ CREATE TABLE `mdm_apple_configuration_profiles` (
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`checksum` binary(16) NOT NULL,
- PRIMARY KEY (`profile_id`),
+ `profile_uuid` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
+ PRIMARY KEY (`profile_uuid`),
UNIQUE KEY `idx_mdm_apple_config_prof_team_identifier` (`team_id`,`identifier`),
- UNIQUE KEY `idx_mdm_apple_config_prof_team_name` (`team_id`,`name`)
+ UNIQUE KEY `idx_mdm_apple_config_prof_team_name` (`team_id`,`name`),
+ UNIQUE KEY `idx_mdm_apple_config_prof_id` (`profile_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client = @@character_set_client */;
@@ -687,7 +689,7 @@ CREATE TABLE `mdm_windows_configuration_profiles` (
`syncml` mediumblob NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `profile_uuid` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
+ `profile_uuid` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
PRIMARY KEY (`profile_uuid`),
UNIQUE KEY `idx_mdm_windows_configuration_profiles_team_id_name` (`team_id`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -722,9 +724,9 @@ CREATE TABLE `migration_status_tables` (
`tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=226 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=227 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
-INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01');
+INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01');
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `mobile_device_management_solutions` (
diff --git a/server/datastore/mysql/teams_test.go b/server/datastore/mysql/teams_test.go
index 8c55ca1faf..5ed6a4ac43 100644
--- a/server/datastore/mysql/teams_test.go
+++ b/server/datastore/mysql/teams_test.go
@@ -86,12 +86,12 @@ func testTeamsGetSetDelete(t *testing.T, ds *Datastore) {
cp, err := ds.NewMDMAppleConfigProfile(context.Background(), dummyCP)
require.NoError(t, err)
- // TODO: once the datastore methods are implemented, use them in tests.
- ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
- _, err := tx.ExecContext(context.Background(),
- `INSERT INTO mdm_windows_configuration_profiles (profile_uuid, name, team_id, syncml) VALUES (uuid(), 'abc', ?, ?)`, team.ID, "")
- return err
+ wcp, err := ds.NewMDMWindowsConfigProfile(context.Background(), fleet.MDMWindowsConfigProfile{
+ Name: "abc",
+ TeamID: &team.ID,
+ SyncML: []byte(``),
})
+ require.NoError(t, err)
err = ds.DeleteTeam(context.Background(), team.ID)
require.NoError(t, err)
@@ -103,17 +103,12 @@ func testTeamsGetSetDelete(t *testing.T, ds *Datastore) {
_, err = ds.TeamByName(context.Background(), tt.name)
require.Error(t, err)
- _, err = ds.GetMDMAppleConfigProfile(context.Background(), cp.ProfileID)
+ _, err = ds.GetMDMAppleConfigProfile(context.Background(), cp.ProfileUUID)
var nfe fleet.NotFoundError
require.ErrorAs(t, err, &nfe)
- // TODO: once the datastore methods are implemented, use them in tests.
- var count int
- ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
- return sqlx.GetContext(context.Background(), tx, &count,
- `SELECT count(*) FROM mdm_windows_configuration_profiles WHERE team_id = ?`, team.ID)
- })
- require.Zero(t, count)
+ _, err = ds.GetMDMWindowsConfigProfile(context.Background(), wcp.ProfileUUID)
+ require.ErrorAs(t, err, &nfe)
require.NoError(t, ds.DeletePack(context.Background(), newP.Name))
})
diff --git a/server/datastore/mysql/testing_utils.go b/server/datastore/mysql/testing_utils.go
index 74a680d278..8cafc7a01a 100644
--- a/server/datastore/mysql/testing_utils.go
+++ b/server/datastore/mysql/testing_utils.go
@@ -427,7 +427,7 @@ func generateDummyWindowsProfile(uuid string) []byte {
// TODO(roberto): update when we have datastore functions and API methods for this
func InsertWindowsProfileForTest(t *testing.T, ds *Datastore, teamID uint) string {
- profUUID := uuid.NewString()
+ profUUID := "w" + uuid.NewString()
prof := generateDummyWindowsProfile(profUUID)
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
stmt := `INSERT INTO mdm_windows_configuration_profiles (profile_uuid, team_id, name, syncml) VALUES (?, ?, ?, ?);`
diff --git a/server/fleet/apple_mdm.go b/server/fleet/apple_mdm.go
index 3c0e187e19..bd8cddca5b 100644
--- a/server/fleet/apple_mdm.go
+++ b/server/fleet/apple_mdm.go
@@ -175,7 +175,12 @@ func (e MDMAppleCommandTimeoutError) StatusCode() int {
// Configuration profiles are used to configure Apple devices .
// See also https://developer.apple.com/documentation/devicemanagement/configuring_multiple_devices_using_profiles.
type MDMAppleConfigProfile struct {
- // ProfileID is the unique id of the configuration profile in Fleet
+ // ProfileUUID is the unique identifier of the configuration profile in
+ // Fleet. For Apple profiles, it is the letter "a" followed by a uuid.
+ ProfileUUID string `db:"profile_uuid" json:"profile_uuid"`
+ // Deprecated: ProfileID is the old unique id of the configuration profile in
+ // Fleet. It is still maintained and generated for new profiles, but only
+ // used in legacy API endpoints.
ProfileID uint `db:"profile_id" json:"profile_id"`
// TeamID is the id of the team with which the configuration is associated. A nil team id
// represents a configuration profile that is not associated with any team.
@@ -227,7 +232,7 @@ func (cp MDMAppleConfigProfile) ValidateUserProvided() error {
type HostMDMAppleProfile struct {
HostUUID string `db:"host_uuid" json:"-"`
CommandUUID string `db:"command_uuid" json:"-"`
- ProfileID uint `db:"profile_id" json:"profile_id"`
+ ProfileUUID string `db:"profile_uuid" json:"profile_uuid"`
Name string `db:"name" json:"name"`
Identifier string `db:"identifier" json:"-"`
Status *MDMDeliveryStatus `db:"status" json:"status"`
@@ -239,7 +244,7 @@ type HostMDMAppleProfile struct {
func (p HostMDMAppleProfile) ToHostMDMProfile() HostMDMProfile {
return HostMDMProfile{
HostUUID: p.HostUUID,
- ProfileID: p.ProfileID,
+ ProfileUUID: p.ProfileUUID,
Name: p.Name,
Identifier: p.Identifier,
Status: p.Status,
@@ -280,7 +285,7 @@ func (d HostMDMProfileDetail) Message() string {
}
type MDMAppleProfilePayload struct {
- ProfileID uint `db:"profile_id"`
+ ProfileUUID string `db:"profile_uuid"`
ProfileIdentifier string `db:"profile_identifier"`
ProfileName string `db:"profile_name"`
HostUUID string `db:"host_uuid"`
@@ -292,7 +297,7 @@ type MDMAppleProfilePayload struct {
}
type MDMAppleBulkUpsertHostProfilePayload struct {
- ProfileID uint
+ ProfileUUID string
ProfileIdentifier string
ProfileName string
HostUUID string
diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go
index b8b0988c4f..8e8153af14 100644
--- a/server/fleet/datastore.go
+++ b/server/fleet/datastore.go
@@ -803,17 +803,25 @@ type Datastore interface {
// this is mainly aimed to internal usage within the Fleet server.
BulkUpsertMDMAppleConfigProfiles(ctx context.Context, payload []*MDMAppleConfigProfile) error
+ // GetMDMAppleConfigProfileByDeprecatedID returns the mdm config profile
+ // corresponding to the specified numeric profile id. This is deprecated and
+ // should not be used for new endpoints.
+ GetMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) (*MDMAppleConfigProfile, error)
// GetMDMAppleConfigProfile returns the mdm config profile corresponding to the specified
- // profile id.
- GetMDMAppleConfigProfile(ctx context.Context, profileID uint) (*MDMAppleConfigProfile, error)
+ // profile uuid.
+ GetMDMAppleConfigProfile(ctx context.Context, profileUUID string) (*MDMAppleConfigProfile, error)
// ListMDMAppleConfigProfiles lists mdm config profiles associated with the specified team id.
// For global config profiles, specify nil as the team id.
ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint) ([]*MDMAppleConfigProfile, error)
+ // DeleteMDMAppleConfigProfileByDeprecatedID deletes the mdm config profile
+ // corresponding to the specified numeric profile id. This is deprecated and
+ // should not be used for new endpoints.
+ DeleteMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) error
// DeleteMDMAppleConfigProfile deletes the mdm config profile corresponding
- // to the specified profile id.
- DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error
+ // to the specified profile uuid.
+ DeleteMDMAppleConfigProfile(ctx context.Context, profileUUID string) error
BulkDeleteMDMAppleHostsConfigProfiles(ctx context.Context, payload []*MDMAppleProfilePayload) error
@@ -929,15 +937,15 @@ type Datastore interface {
// status of a profile in a host.
BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload []*MDMAppleBulkUpsertHostProfilePayload) error
- // BulkSetPendingMDMHostProfiles sets the status of profiles to install
- // or to remove for each affected host to pending for the provided criteria,
- // which may be either a list of hostIDs, teamIDs, profileIDs or hostUUIDs
- // (only one of those ID types can be provided).
- BulkSetPendingMDMHostProfiles(ctx context.Context, hostIDs, teamIDs, profileIDs []uint, profileUUIDs, hostUUIDs []string) error
+ // BulkSetPendingMDMHostProfiles sets the status of profiles to install or to
+ // remove for each affected host to pending for the provided criteria, which
+ // may be either a list of hostIDs, teamIDs, profileUUIDs or hostUUIDs (only
+ // one of those ID types can be provided).
+ BulkSetPendingMDMHostProfiles(ctx context.Context, hostIDs, teamIDs []uint, profileUUIDs, hostUUIDs []string) error
// GetMDMAppleProfilesContents retrieves the XML contents of the
// profiles requested.
- GetMDMAppleProfilesContents(ctx context.Context, profileIDs []uint) (map[uint]mobileconfig.Mobileconfig, error)
+ GetMDMAppleProfilesContents(ctx context.Context, profileUUIDs []string) (map[string]mobileconfig.Mobileconfig, error)
// UpdateOrDeleteHostMDMAppleProfile updates information about a single
// profile status. It deletes the row if the profile operation is "remove"
@@ -1123,7 +1131,7 @@ type Datastore interface {
// ListMDMWindowsProfilesToRemove returns all the profiles that should
// be removed based on diffing the ideal state vs the state we have
- // registered in `host_mdm_apple_profiles`
+ // registered in `host_mdm_windows_profiles`
ListMDMWindowsProfilesToRemove(ctx context.Context) ([]*MDMWindowsProfilePayload, error)
// BulkUpsertMDMWindowsHostProfiles bulk-adds/updates records to track the
diff --git a/server/fleet/mdm.go b/server/fleet/mdm.go
index b0f4dd22ad..a2e07182d5 100644
--- a/server/fleet/mdm.go
+++ b/server/fleet/mdm.go
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net/url"
- "strconv"
"time"
)
@@ -268,11 +267,11 @@ type MDMProfilesSummary struct {
}
// HostMDMProfile is the status of an MDM profile on a host. It can be used to represent either
-// a Windows or macOS profile. The ProfileID field is a string for Windows and an integer for macOS.
+// a Windows or macOS profile.
type HostMDMProfile struct {
HostUUID string `db:"-" json:"-"`
CommandUUID string `db:"-" json:"-"`
- ProfileID interface{} `db:"-" json:"profile_id"`
+ ProfileUUID string `db:"-" json:"profile_uuid"`
Name string `db:"-" json:"name"`
Identifier string `db:"-" json:"-"`
Status *MDMDeliveryStatus `db:"-" json:"status"`
@@ -352,14 +351,14 @@ func (m MDMConfigProfileAuthz) AuthzType() string {
// MDMConfigProfilePayload is the platform-agnostic struct returned by
// endpoints that return MDM configuration profiles (get/list profiles).
type MDMConfigProfilePayload struct {
- ProfileID string `json:"profile_id" db:"profile_id"` // is a uuid string for Windows
- TeamID *uint `json:"team_id" db:"team_id"` // null for no-team
- Name string `json:"name" db:"name"`
- Platform string `json:"platform" db:"platform"` // "windows" or "darwin"
- Identifier string `json:"identifier,omitempty" db:"identifier"` // only set for macOS
- Checksum []byte `json:"checksum,omitempty" db:"checksum"` // only set for macOS
- CreatedAt time.Time `json:"created_at" db:"created_at"`
- UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
+ ProfileUUID string `json:"profile_uuid" db:"profile_uuid"`
+ TeamID *uint `json:"team_id" db:"team_id"` // null for no-team
+ Name string `json:"name" db:"name"`
+ Platform string `json:"platform" db:"platform"` // "windows" or "darwin"
+ Identifier string `json:"identifier,omitempty" db:"identifier"` // only set for macOS
+ Checksum []byte `json:"checksum,omitempty" db:"checksum"` // only set for macOS
+ CreatedAt time.Time `json:"created_at" db:"created_at"`
+ UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
func NewMDMConfigProfilePayloadFromWindows(cp *MDMWindowsConfigProfile) *MDMConfigProfilePayload {
@@ -368,12 +367,12 @@ func NewMDMConfigProfilePayloadFromWindows(cp *MDMWindowsConfigProfile) *MDMConf
tid = cp.TeamID
}
return &MDMConfigProfilePayload{
- ProfileID: cp.ProfileUUID,
- TeamID: tid,
- Name: cp.Name,
- Platform: "windows",
- CreatedAt: cp.CreatedAt,
- UpdatedAt: cp.UpdatedAt,
+ ProfileUUID: cp.ProfileUUID,
+ TeamID: tid,
+ Name: cp.Name,
+ Platform: "windows",
+ CreatedAt: cp.CreatedAt,
+ UpdatedAt: cp.UpdatedAt,
}
}
@@ -383,13 +382,13 @@ func NewMDMConfigProfilePayloadFromApple(cp *MDMAppleConfigProfile) *MDMConfigPr
tid = cp.TeamID
}
return &MDMConfigProfilePayload{
- ProfileID: strconv.FormatUint(uint64(cp.ProfileID), 10),
- TeamID: tid,
- Name: cp.Name,
- Identifier: cp.Identifier,
- Platform: "darwin",
- Checksum: cp.Checksum,
- CreatedAt: cp.CreatedAt,
- UpdatedAt: cp.UpdatedAt,
+ ProfileUUID: cp.ProfileUUID,
+ TeamID: tid,
+ Name: cp.Name,
+ Identifier: cp.Identifier,
+ Platform: "darwin",
+ Checksum: cp.Checksum,
+ CreatedAt: cp.CreatedAt,
+ UpdatedAt: cp.UpdatedAt,
}
}
diff --git a/server/fleet/microsoft_mdm.go b/server/fleet/microsoft_mdm.go
index 752b67d49e..c910f37911 100644
--- a/server/fleet/microsoft_mdm.go
+++ b/server/fleet/microsoft_mdm.go
@@ -1394,7 +1394,7 @@ type HostMDMWindowsProfile struct {
func (p HostMDMWindowsProfile) ToHostMDMProfile() HostMDMProfile {
return HostMDMProfile{
HostUUID: p.HostUUID,
- ProfileID: p.ProfileUUID,
+ ProfileUUID: p.ProfileUUID,
Name: p.Name,
Identifier: "",
Status: p.Status,
diff --git a/server/fleet/service.go b/server/fleet/service.go
index bfeaaaa404..93e988a895 100644
--- a/server/fleet/service.go
+++ b/server/fleet/service.go
@@ -600,10 +600,18 @@ type Service interface {
// NewMDMAppleConfigProfile creates a new configuration profile for the specified team.
NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r io.Reader) (*MDMAppleConfigProfile, error)
+ // GetMDMAppleConfigProfileByDeprecatedID retrieves the specified Apple
+ // configuration profile via its numeric ID. This method is deprecated and
+ // should not be used for new endpoints.
+ GetMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) (*MDMAppleConfigProfile, error)
// GetMDMAppleConfigProfile retrieves the specified configuration profile.
- GetMDMAppleConfigProfile(ctx context.Context, profileID uint) (*MDMAppleConfigProfile, error)
+ GetMDMAppleConfigProfile(ctx context.Context, profileUUID string) (*MDMAppleConfigProfile, error)
+ // DeleteMDMAppleConfigProfileByDeprecatedID deletes the specified Apple
+ // configuration profile via its numeric ID. This method is deprecated and
+ // should not be used for new endpoints.
+ DeleteMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) error
// DeleteMDMAppleConfigProfile deletes the specified configuration profile.
- DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error
+ DeleteMDMAppleConfigProfile(ctx context.Context, profileUUID string) error
// ListMDMAppleConfigProfiles returns the list of all the configuration profiles for the
// specified team.
ListMDMAppleConfigProfiles(ctx context.Context, teamID uint) ([]*MDMAppleConfigProfile, error)
diff --git a/server/fleet/windows_mdm.go b/server/fleet/windows_mdm.go
index 7901449624..36509791c8 100644
--- a/server/fleet/windows_mdm.go
+++ b/server/fleet/windows_mdm.go
@@ -29,6 +29,8 @@ type MDMWindowsBitLockerSummary struct {
// MDMWindowsConfigProfile represents a Windows MDM profile in Fleet.
type MDMWindowsConfigProfile struct {
+ // ProfileUUID is the unique identifier of the configuration profile in
+ // Fleet. For Windows profiles, it is the letter "w" followed by a uuid.
ProfileUUID string `db:"profile_uuid" json:"profile_uuid"`
TeamID *uint `db:"team_id" json:"team_id"`
Name string `db:"name" json:"name"`
diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go
index ca16773f76..33a87fb82e 100644
--- a/server/mock/datastore_mock.go
+++ b/server/mock/datastore_mock.go
@@ -548,11 +548,15 @@ type NewMDMAppleConfigProfileFunc func(ctx context.Context, p fleet.MDMAppleConf
type BulkUpsertMDMAppleConfigProfilesFunc func(ctx context.Context, payload []*fleet.MDMAppleConfigProfile) error
-type GetMDMAppleConfigProfileFunc func(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error)
+type GetMDMAppleConfigProfileByDeprecatedIDFunc func(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error)
+
+type GetMDMAppleConfigProfileFunc func(ctx context.Context, profileUUID string) (*fleet.MDMAppleConfigProfile, error)
type ListMDMAppleConfigProfilesFunc func(ctx context.Context, teamID *uint) ([]*fleet.MDMAppleConfigProfile, error)
-type DeleteMDMAppleConfigProfileFunc func(ctx context.Context, profileID uint) error
+type DeleteMDMAppleConfigProfileByDeprecatedIDFunc func(ctx context.Context, profileID uint) error
+
+type DeleteMDMAppleConfigProfileFunc func(ctx context.Context, profileUUID string) error
type BulkDeleteMDMAppleHostsConfigProfilesFunc func(ctx context.Context, payload []*fleet.MDMAppleProfilePayload) error
@@ -618,9 +622,9 @@ type ListMDMAppleProfilesToRemoveFunc func(ctx context.Context) ([]*fleet.MDMApp
type BulkUpsertMDMAppleHostProfilesFunc func(ctx context.Context, payload []*fleet.MDMAppleBulkUpsertHostProfilePayload) error
-type BulkSetPendingMDMHostProfilesFunc func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileIDs []uint, profileUUIDs []string, hostUUIDs []string) error
+type BulkSetPendingMDMHostProfilesFunc func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileUUIDs []string, hostUUIDs []string) error
-type GetMDMAppleProfilesContentsFunc func(ctx context.Context, profileIDs []uint) (map[uint]mobileconfig.Mobileconfig, error)
+type GetMDMAppleProfilesContentsFunc func(ctx context.Context, profileUUIDs []string) (map[string]mobileconfig.Mobileconfig, error)
type UpdateOrDeleteHostMDMAppleProfileFunc func(ctx context.Context, profile *fleet.HostMDMAppleProfile) error
@@ -1554,12 +1558,18 @@ type DataStore struct {
BulkUpsertMDMAppleConfigProfilesFunc BulkUpsertMDMAppleConfigProfilesFunc
BulkUpsertMDMAppleConfigProfilesFuncInvoked bool
+ GetMDMAppleConfigProfileByDeprecatedIDFunc GetMDMAppleConfigProfileByDeprecatedIDFunc
+ GetMDMAppleConfigProfileByDeprecatedIDFuncInvoked bool
+
GetMDMAppleConfigProfileFunc GetMDMAppleConfigProfileFunc
GetMDMAppleConfigProfileFuncInvoked bool
ListMDMAppleConfigProfilesFunc ListMDMAppleConfigProfilesFunc
ListMDMAppleConfigProfilesFuncInvoked bool
+ DeleteMDMAppleConfigProfileByDeprecatedIDFunc DeleteMDMAppleConfigProfileByDeprecatedIDFunc
+ DeleteMDMAppleConfigProfileByDeprecatedIDFuncInvoked bool
+
DeleteMDMAppleConfigProfileFunc DeleteMDMAppleConfigProfileFunc
DeleteMDMAppleConfigProfileFuncInvoked bool
@@ -3727,11 +3737,18 @@ func (s *DataStore) BulkUpsertMDMAppleConfigProfiles(ctx context.Context, payloa
return s.BulkUpsertMDMAppleConfigProfilesFunc(ctx, payload)
}
-func (s *DataStore) GetMDMAppleConfigProfile(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
+func (s *DataStore) GetMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
+ s.mu.Lock()
+ s.GetMDMAppleConfigProfileByDeprecatedIDFuncInvoked = true
+ s.mu.Unlock()
+ return s.GetMDMAppleConfigProfileByDeprecatedIDFunc(ctx, profileID)
+}
+
+func (s *DataStore) GetMDMAppleConfigProfile(ctx context.Context, profileUUID string) (*fleet.MDMAppleConfigProfile, error) {
s.mu.Lock()
s.GetMDMAppleConfigProfileFuncInvoked = true
s.mu.Unlock()
- return s.GetMDMAppleConfigProfileFunc(ctx, profileID)
+ return s.GetMDMAppleConfigProfileFunc(ctx, profileUUID)
}
func (s *DataStore) ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint) ([]*fleet.MDMAppleConfigProfile, error) {
@@ -3741,11 +3758,18 @@ func (s *DataStore) ListMDMAppleConfigProfiles(ctx context.Context, teamID *uint
return s.ListMDMAppleConfigProfilesFunc(ctx, teamID)
}
-func (s *DataStore) DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error {
+func (s *DataStore) DeleteMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) error {
+ s.mu.Lock()
+ s.DeleteMDMAppleConfigProfileByDeprecatedIDFuncInvoked = true
+ s.mu.Unlock()
+ return s.DeleteMDMAppleConfigProfileByDeprecatedIDFunc(ctx, profileID)
+}
+
+func (s *DataStore) DeleteMDMAppleConfigProfile(ctx context.Context, profileUUID string) error {
s.mu.Lock()
s.DeleteMDMAppleConfigProfileFuncInvoked = true
s.mu.Unlock()
- return s.DeleteMDMAppleConfigProfileFunc(ctx, profileID)
+ return s.DeleteMDMAppleConfigProfileFunc(ctx, profileUUID)
}
func (s *DataStore) BulkDeleteMDMAppleHostsConfigProfiles(ctx context.Context, payload []*fleet.MDMAppleProfilePayload) error {
@@ -3972,18 +3996,18 @@ func (s *DataStore) BulkUpsertMDMAppleHostProfiles(ctx context.Context, payload
return s.BulkUpsertMDMAppleHostProfilesFunc(ctx, payload)
}
-func (s *DataStore) BulkSetPendingMDMHostProfiles(ctx context.Context, hostIDs []uint, teamIDs []uint, profileIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
+func (s *DataStore) BulkSetPendingMDMHostProfiles(ctx context.Context, hostIDs []uint, teamIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
s.mu.Lock()
s.BulkSetPendingMDMHostProfilesFuncInvoked = true
s.mu.Unlock()
- return s.BulkSetPendingMDMHostProfilesFunc(ctx, hostIDs, teamIDs, profileIDs, profileUUIDs, hostUUIDs)
+ return s.BulkSetPendingMDMHostProfilesFunc(ctx, hostIDs, teamIDs, profileUUIDs, hostUUIDs)
}
-func (s *DataStore) GetMDMAppleProfilesContents(ctx context.Context, profileIDs []uint) (map[uint]mobileconfig.Mobileconfig, error) {
+func (s *DataStore) GetMDMAppleProfilesContents(ctx context.Context, profileUUIDs []string) (map[string]mobileconfig.Mobileconfig, error) {
s.mu.Lock()
s.GetMDMAppleProfilesContentsFuncInvoked = true
s.mu.Unlock()
- return s.GetMDMAppleProfilesContentsFunc(ctx, profileIDs)
+ return s.GetMDMAppleProfilesContentsFunc(ctx, profileUUIDs)
}
func (s *DataStore) UpdateOrDeleteHostMDMAppleProfile(ctx context.Context, profile *fleet.HostMDMAppleProfile) error {
diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go
index 674fa5ea47..5abeb2af0e 100644
--- a/server/service/apple_mdm.go
+++ b/server/service/apple_mdm.go
@@ -356,7 +356,7 @@ func (svc *Service) NewMDMAppleConfigProfile(ctx context.Context, teamID uint, r
}
return nil, ctxerr.Wrap(ctx, err)
}
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []uint{newCP.ProfileID}, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newCP.ProfileUUID}, nil); err != nil {
return nil, ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
@@ -461,7 +461,7 @@ func (r getMDMAppleConfigProfileResponse) hijackRender(ctx context.Context, w ht
func getMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
req := request.(*getMDMAppleConfigProfileRequest)
- cp, err := svc.GetMDMAppleConfigProfile(ctx, req.ProfileID)
+ cp, err := svc.GetMDMAppleConfigProfileByDeprecatedID(ctx, req.ProfileID)
if err != nil {
return getMDMAppleConfigProfileResponse{Err: err}, nil
}
@@ -471,13 +471,31 @@ func getMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{},
return getMDMAppleConfigProfileResponse{fileReader: io.NopCloser(reader), fileLength: reader.Size(), fileName: fileName}, nil
}
-func (svc *Service) GetMDMAppleConfigProfile(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
+func (svc *Service) GetMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
// first we perform a perform basic authz check
if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil {
return nil, err
}
- cp, err := svc.ds.GetMDMAppleConfigProfile(ctx, profileID)
+ cp, err := svc.ds.GetMDMAppleConfigProfileByDeprecatedID(ctx, profileID)
+ if err != nil {
+ if fleet.IsNotFound(err) {
+ // call the standard service method with a profile UUID that will not be
+ // found, just to ensure the same sequence of validations are applied.
+ return svc.GetMDMAppleConfigProfile(ctx, "-")
+ }
+ return nil, ctxerr.Wrap(ctx, err)
+ }
+ return svc.GetMDMAppleConfigProfile(ctx, cp.ProfileUUID)
+}
+
+func (svc *Service) GetMDMAppleConfigProfile(ctx context.Context, profileUUID string) (*fleet.MDMAppleConfigProfile, error) {
+ // first we perform a perform basic authz check
+ if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil {
+ return nil, err
+ }
+
+ cp, err := svc.ds.GetMDMAppleConfigProfile(ctx, profileUUID)
if err != nil {
return nil, ctxerr.Wrap(ctx, err)
}
@@ -503,14 +521,33 @@ func (r deleteMDMAppleConfigProfileResponse) error() error { return r.Err }
func deleteMDMAppleConfigProfileEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
req := request.(*deleteMDMAppleConfigProfileRequest)
- if err := svc.DeleteMDMAppleConfigProfile(ctx, req.ProfileID); err != nil {
+ if err := svc.DeleteMDMAppleConfigProfileByDeprecatedID(ctx, req.ProfileID); err != nil {
return &deleteMDMAppleConfigProfileResponse{Err: err}, nil
}
return &deleteMDMAppleConfigProfileResponse{}, nil
}
-func (svc *Service) DeleteMDMAppleConfigProfile(ctx context.Context, profileID uint) error {
+func (svc *Service) DeleteMDMAppleConfigProfileByDeprecatedID(ctx context.Context, profileID uint) error {
+ // first we perform a perform basic authz check
+ if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil {
+ return ctxerr.Wrap(ctx, err)
+ }
+
+ // get the profile by ID and call the standard delete function
+ cp, err := svc.ds.GetMDMAppleConfigProfileByDeprecatedID(ctx, profileID)
+ if err != nil {
+ if fleet.IsNotFound(err) {
+ // call the standard service method with a profile UUID that will not be
+ // found, just to ensure the same sequence of validations are applied.
+ return svc.DeleteMDMAppleConfigProfile(ctx, "-")
+ }
+ return ctxerr.Wrap(ctx, err)
+ }
+ return svc.DeleteMDMAppleConfigProfile(ctx, cp.ProfileUUID)
+}
+
+func (svc *Service) DeleteMDMAppleConfigProfile(ctx context.Context, profileUUID string) error {
// first we perform a perform basic authz check
if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil {
return ctxerr.Wrap(ctx, err)
@@ -519,11 +556,11 @@ func (svc *Service) DeleteMDMAppleConfigProfile(ctx context.Context, profileID u
// check that Apple MDM is enabled - the middleware of that endpoint checks
// only that any MDM is enabled, maybe it's just Windows
if err := svc.VerifyMDMAppleConfigured(ctx); err != nil {
- err := fleet.NewInvalidArgumentError("profile_id", fleet.AppleMDMNotConfiguredMessage).WithStatus(http.StatusBadRequest)
+ err := fleet.NewInvalidArgumentError("profile_uuid", fleet.AppleMDMNotConfiguredMessage).WithStatus(http.StatusBadRequest)
return ctxerr.Wrap(ctx, err, "check macOS MDM enabled")
}
- cp, err := svc.ds.GetMDMAppleConfigProfile(ctx, profileID)
+ cp, err := svc.ds.GetMDMAppleConfigProfile(ctx, profileUUID)
if err != nil {
return ctxerr.Wrap(ctx, err)
}
@@ -551,11 +588,11 @@ func (svc *Service) DeleteMDMAppleConfigProfile(ctx context.Context, profileID u
}
}
- if err := svc.ds.DeleteMDMAppleConfigProfile(ctx, profileID); err != nil {
+ if err := svc.ds.DeleteMDMAppleConfigProfile(ctx, profileUUID); err != nil {
return ctxerr.Wrap(ctx, err)
}
// cannot use the profile ID as it is now deleted
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{teamID}, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{teamID}, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
@@ -1495,7 +1532,7 @@ func (svc *Service) BatchSetMDMAppleProfiles(ctx context.Context, tmID *uint, tm
}
if !skipBulkPending {
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{bulkTeamID}, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{bulkTeamID}, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
}
@@ -2187,7 +2224,7 @@ func (svc *MDMAppleCheckinAndCommandService) TokenUpdate(r *mdm.Request, m *mdm.
if nanoEnroll != nil && nanoEnroll.Enabled &&
nanoEnroll.Type == "Device" && nanoEnroll.TokenUpdateTally == 1 {
// device is enrolled for the first time, not a token update
- if err := svc.ds.BulkSetPendingMDMHostProfiles(r.Context, nil, nil, nil, nil, []string{r.ID}); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(r.Context, nil, nil, nil, []string{r.ID}); err != nil {
return err
}
@@ -2442,11 +2479,11 @@ func ReconcileAppleProfiles(
// Perform aggregations to support all the operations we need to do
- // toGetContents contains the IDs of all the profiles from which we
+ // toGetContents contains the UUIDs of all the profiles from which we
// need to retrieve contents. Since the previous query returns one row
// per host, it would be too expensive to retrieve the profile contents
// there, so we make another request. Using a map to deduplicate.
- toGetContents := make(map[uint]bool)
+ toGetContents := make(map[string]bool)
// hostProfiles tracks each host_mdm_apple_profile we need to upsert
// with the new status, operation_type, etc.
@@ -2468,7 +2505,7 @@ func ReconcileAppleProfiles(
// command.
hostProfilesToCleanup := []*fleet.MDMAppleProfilePayload{}
- // install/removeTargets are maps from profileID -> command uuid and host
+ // install/removeTargets are maps from profileUUID -> command uuid and host
// UUIDs as the underlying MDM services are optimized to send one command to
// multiple hosts at the same time. Note that the same command uuid is used
// for all hosts in a given install/remove target operation.
@@ -2477,7 +2514,7 @@ func ReconcileAppleProfiles(
profIdent string
hostUUIDs []string
}
- installTargets, removeTargets := make(map[uint]*cmdTarget), make(map[uint]*cmdTarget)
+ installTargets, removeTargets := make(map[string]*cmdTarget), make(map[string]*cmdTarget)
for _, p := range toInstall {
if pp, ok := profileIntersection.GetMatchingProfileInCurrentState(p); ok {
// if the profile was in any other status than `failed`
@@ -2486,7 +2523,7 @@ func ReconcileAppleProfiles(
// command.
if pp.Status != &fleet.MDMDeliveryFailed && bytes.Equal(pp.Checksum, p.Checksum) {
hostProfiles = append(hostProfiles, &fleet.MDMAppleBulkUpsertHostProfilePayload{
- ProfileID: p.ProfileID,
+ ProfileUUID: p.ProfileUUID,
HostUUID: p.HostUUID,
ProfileIdentifier: p.ProfileIdentifier,
ProfileName: p.ProfileName,
@@ -2499,20 +2536,20 @@ func ReconcileAppleProfiles(
continue
}
}
- toGetContents[p.ProfileID] = true
+ toGetContents[p.ProfileUUID] = true
- target := installTargets[p.ProfileID]
+ target := installTargets[p.ProfileUUID]
if target == nil {
target = &cmdTarget{
cmdUUID: uuid.New().String(),
profIdent: p.ProfileIdentifier,
}
- installTargets[p.ProfileID] = target
+ installTargets[p.ProfileUUID] = target
}
target.hostUUIDs = append(target.hostUUIDs, p.HostUUID)
hostProfiles = append(hostProfiles, &fleet.MDMAppleBulkUpsertHostProfilePayload{
- ProfileID: p.ProfileID,
+ ProfileUUID: p.ProfileUUID,
HostUUID: p.HostUUID,
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryPending,
@@ -2529,18 +2566,18 @@ func ReconcileAppleProfiles(
continue
}
- target := removeTargets[p.ProfileID]
+ target := removeTargets[p.ProfileUUID]
if target == nil {
target = &cmdTarget{
cmdUUID: uuid.New().String(),
profIdent: p.ProfileIdentifier,
}
- removeTargets[p.ProfileID] = target
+ removeTargets[p.ProfileUUID] = target
}
target.hostUUIDs = append(target.hostUUIDs, p.HostUUID)
hostProfiles = append(hostProfiles, &fleet.MDMAppleBulkUpsertHostProfilePayload{
- ProfileID: p.ProfileID,
+ ProfileUUID: p.ProfileUUID,
HostUUID: p.HostUUID,
OperationType: fleet.MDMOperationTypeRemove,
Status: &fleet.MDMDeliveryPending,
@@ -2571,11 +2608,11 @@ func ReconcileAppleProfiles(
}
// Grab the contents of all the profiles we need to install
- profileIDs := make([]uint, 0, len(toGetContents))
- for pid := range toGetContents {
- profileIDs = append(profileIDs, pid)
+ profileUUIDs := make([]string, 0, len(toGetContents))
+ for pUUID := range toGetContents {
+ profileUUIDs = append(profileUUIDs, pUUID)
}
- profileContents, err := ds.GetMDMAppleProfilesContents(ctx, profileIDs)
+ profileContents, err := ds.GetMDMAppleProfilesContents(ctx, profileUUIDs)
if err != nil {
return ctxerr.Wrap(ctx, err, "get profile contents")
}
@@ -2589,13 +2626,13 @@ func ReconcileAppleProfiles(
var wgProd, wgCons sync.WaitGroup
ch := make(chan remoteResult)
- execCmd := func(profID uint, target *cmdTarget, op fleet.MDMOperationType) {
+ execCmd := func(profUUID string, target *cmdTarget, op fleet.MDMOperationType) {
defer wgProd.Done()
var err error
switch op {
case fleet.MDMOperationTypeInstall:
- err = commander.InstallProfile(ctx, target.hostUUIDs, profileContents[profID], target.cmdUUID)
+ err = commander.InstallProfile(ctx, target.hostUUIDs, profileContents[profUUID], target.cmdUUID)
case fleet.MDMOperationTypeRemove:
err = commander.RemoveProfile(ctx, target.hostUUIDs, target.profIdent, target.cmdUUID)
}
@@ -2609,13 +2646,13 @@ func ReconcileAppleProfiles(
ch <- remoteResult{err, target.cmdUUID}
}
}
- for profID, target := range installTargets {
+ for profUUID, target := range installTargets {
wgProd.Add(1)
- go execCmd(profID, target, fleet.MDMOperationTypeInstall)
+ go execCmd(profUUID, target, fleet.MDMOperationTypeInstall)
}
- for profID, target := range removeTargets {
+ for profUUID, target := range removeTargets {
wgProd.Add(1)
- go execCmd(profID, target, fleet.MDMOperationTypeRemove)
+ go execCmd(profUUID, target, fleet.MDMOperationTypeRemove)
}
// index the host profiles by cmdUUID, for ease of error processing in the
diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go
index 73c9682185..26546e85e4 100644
--- a/server/service/apple_mdm_test.go
+++ b/server/service/apple_mdm_test.go
@@ -467,6 +467,7 @@ func TestAppleMDMAuthorization(t *testing.T) {
func TestMDMAppleConfigProfileAuthz(t *testing.T) {
svc, ctx, ds := setupAppleMDMService(t, &fleet.LicenseInfo{Tier: fleet.TierPremium})
+ profUUID := "a" + uuid.NewString()
testCases := []struct {
name string
user *fleet.User
@@ -547,18 +548,18 @@ func TestMDMAppleConfigProfileAuthz(t *testing.T) {
ds.GetMDMAppleProfilesSummaryFunc = func(context.Context, *uint) (*fleet.MDMProfilesSummary, error) {
return nil, nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
mockGetFuncWithTeamID := func(teamID uint) mock.GetMDMAppleConfigProfileFunc {
- return func(ctx context.Context, profileID uint) (*fleet.MDMAppleConfigProfile, error) {
- require.Equal(t, uint(42), profileID)
+ return func(ctx context.Context, puid string) (*fleet.MDMAppleConfigProfile, error) {
+ require.Equal(t, profUUID, puid)
return &fleet.MDMAppleConfigProfile{TeamID: &teamID}, nil
}
}
mockDeleteFuncWithTeamID := func(teamID uint) mock.DeleteMDMAppleConfigProfileFunc {
- return func(ctx context.Context, profileID uint) error {
- require.Equal(t, uint(42), profileID)
+ return func(ctx context.Context, puid string) error {
+ require.Equal(t, profUUID, puid)
return nil
}
}
@@ -609,22 +610,22 @@ func TestMDMAppleConfigProfileAuthz(t *testing.T) {
// test authz get config profile (no team)
ds.GetMDMAppleConfigProfileFunc = mockGetFuncWithTeamID(0)
- _, err = svc.GetMDMAppleConfigProfile(ctx, 42)
+ _, err = svc.GetMDMAppleConfigProfile(ctx, profUUID)
checkShouldFail(err, tt.shouldFailGlobal)
// test authz delete config profile (no team)
ds.DeleteMDMAppleConfigProfileFunc = mockDeleteFuncWithTeamID(0)
- err = svc.DeleteMDMAppleConfigProfile(ctx, 42)
+ err = svc.DeleteMDMAppleConfigProfile(ctx, profUUID)
checkShouldFail(err, tt.shouldFailGlobal)
// test authz get config profile (team 1)
ds.GetMDMAppleConfigProfileFunc = mockGetFuncWithTeamID(1)
- _, err = svc.GetMDMAppleConfigProfile(ctx, 42)
+ _, err = svc.GetMDMAppleConfigProfile(ctx, profUUID)
checkShouldFail(err, tt.shouldFailTeam)
// test authz delete config profile (team 1)
ds.DeleteMDMAppleConfigProfileFunc = mockDeleteFuncWithTeamID(1)
- err = svc.DeleteMDMAppleConfigProfile(ctx, 42)
+ err = svc.DeleteMDMAppleConfigProfile(ctx, profUUID)
checkShouldFail(err, tt.shouldFailTeam)
// test authz get profiles summary (no team)
@@ -649,13 +650,12 @@ func TestNewMDMAppleConfigProfile(t *testing.T) {
require.Equal(t, "Foo", cp.Name)
require.Equal(t, "Bar", cp.Identifier)
require.Equal(t, mcBytes, []byte(cp.Mobileconfig))
- cp.ProfileID = 1
return &cp, nil
}
ds.NewActivityFunc = func(context.Context, *fleet.User, fleet.ActivityDetails) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
@@ -693,13 +693,9 @@ func TestHostDetailsMDMProfiles(t *testing.T) {
ctx = viewer.NewContext(ctx, viewer.Viewer{User: &fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)}})
expected := []fleet.HostMDMAppleProfile{
- {HostUUID: "H057-UU1D-1337", Name: "NAME-5", ProfileID: uint(5), CommandUUID: "CMD-UU1D-5", Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
- {HostUUID: "H057-UU1D-1337", Name: "NAME-9", ProfileID: uint(8), CommandUUID: "CMD-UU1D-8", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
- {HostUUID: "H057-UU1D-1337", Name: "NAME-13", ProfileID: uint(13), CommandUUID: "CMD-UU1D-13", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
- }
- expectedByProfileID := make(map[uint]fleet.HostMDMAppleProfile)
- for _, ep := range expected {
- expectedByProfileID[ep.ProfileID] = ep
+ {HostUUID: "H057-UU1D-1337", Name: "NAME-5", ProfileUUID: "a" + uuid.NewString(), CommandUUID: "CMD-UU1D-5", Status: &fleet.MDMDeliveryPending, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
+ {HostUUID: "H057-UU1D-1337", Name: "NAME-9", ProfileUUID: "a" + uuid.NewString(), CommandUUID: "CMD-UU1D-8", Status: &fleet.MDMDeliveryVerifying, OperationType: fleet.MDMOperationTypeInstall, Detail: ""},
+ {HostUUID: "H057-UU1D-1337", Name: "NAME-13", ProfileUUID: "a" + uuid.NewString(), CommandUUID: "CMD-UU1D-13", Status: &fleet.MDMDeliveryFailed, OperationType: fleet.MDMOperationTypeRemove, Detail: "Error removing profile"},
}
ds.GetHostMDMAppleProfilesFunc = func(ctx context.Context, hostUUID string) ([]fleet.HostMDMAppleProfile, error) {
@@ -1057,7 +1053,7 @@ func TestMDMTokenUpdate(t *testing.T) {
}, nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
@@ -1300,7 +1296,7 @@ func TestMDMBatchSetAppleProfiles(t *testing.T) {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
@@ -1611,7 +1607,7 @@ func TestMDMBatchSetAppleProfilesBoolArgs(t *testing.T) {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, profileUUIDs, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, profileUUIDs, uuids []string) error {
return nil
}
@@ -2051,29 +2047,30 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
contents4 := []byte("test-content-4")
contents4Base64 := base64.StdEncoding.EncodeToString(contents4)
+ p1, p2, p3, p4 := "a"+uuid.NewString(), "a"+uuid.NewString(), "a"+uuid.NewString(), "a"+uuid.NewString()
ds.ListMDMAppleProfilesToInstallFunc = func(ctx context.Context) ([]*fleet.MDMAppleProfilePayload, error) {
return []*fleet.MDMAppleProfilePayload{
- {ProfileID: 1, ProfileIdentifier: "com.add.profile", HostUUID: hostUUID},
- {ProfileID: 2, ProfileIdentifier: "com.add.profile.two", HostUUID: hostUUID},
- {ProfileID: 2, ProfileIdentifier: "com.add.profile.two", HostUUID: hostUUID2},
- {ProfileID: 4, ProfileIdentifier: "com.add.profile.four", HostUUID: hostUUID2},
+ {ProfileUUID: p1, ProfileIdentifier: "com.add.profile", HostUUID: hostUUID},
+ {ProfileUUID: p2, ProfileIdentifier: "com.add.profile.two", HostUUID: hostUUID},
+ {ProfileUUID: p2, ProfileIdentifier: "com.add.profile.two", HostUUID: hostUUID2},
+ {ProfileUUID: p4, ProfileIdentifier: "com.add.profile.four", HostUUID: hostUUID2},
}, nil
}
ds.ListMDMAppleProfilesToRemoveFunc = func(ctx context.Context) ([]*fleet.MDMAppleProfilePayload, error) {
return []*fleet.MDMAppleProfilePayload{
- {ProfileID: 3, ProfileIdentifier: "com.remove.profile", HostUUID: hostUUID},
- {ProfileID: 3, ProfileIdentifier: "com.remove.profile", HostUUID: hostUUID2},
+ {ProfileUUID: p3, ProfileIdentifier: "com.remove.profile", HostUUID: hostUUID},
+ {ProfileUUID: p3, ProfileIdentifier: "com.remove.profile", HostUUID: hostUUID2},
}, nil
}
- ds.GetMDMAppleProfilesContentsFunc = func(ctx context.Context, profileIDs []uint) (map[uint]mobileconfig.Mobileconfig, error) {
- require.ElementsMatch(t, []uint{1, 2, 4}, profileIDs)
+ ds.GetMDMAppleProfilesContentsFunc = func(ctx context.Context, profileUUIDs []string) (map[string]mobileconfig.Mobileconfig, error) {
+ require.ElementsMatch(t, []string{p1, p2, p4}, profileUUIDs)
// only those profiles that are to be installed
- return map[uint]mobileconfig.Mobileconfig{
- 1: contents1,
- 2: contents2,
- 4: contents4,
+ return map[string]mobileconfig.Mobileconfig{
+ p1: contents1,
+ p2: contents2,
+ p4: contents4,
}, nil
}
@@ -2146,24 +2143,24 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
// first time it is called, it is to set the status to pending and all
// host profiles have a command uuid
- cmdUUIDByProfileIDInstall := make(map[uint]string)
- cmdUUIDByProfileIDRemove := make(map[uint]string)
+ cmdUUIDByProfileUUIDInstall := make(map[string]string)
+ cmdUUIDByProfileUUIDRemove := make(map[string]string)
copies := make([]*fleet.MDMAppleBulkUpsertHostProfilePayload, len(payload))
for i, p := range payload {
if p.OperationType == fleet.MDMOperationTypeInstall {
- existing, ok := cmdUUIDByProfileIDInstall[p.ProfileID]
+ existing, ok := cmdUUIDByProfileUUIDInstall[p.ProfileUUID]
if ok {
require.Equal(t, existing, p.CommandUUID)
} else {
- cmdUUIDByProfileIDInstall[p.ProfileID] = p.CommandUUID
+ cmdUUIDByProfileUUIDInstall[p.ProfileUUID] = p.CommandUUID
}
} else {
require.Equal(t, fleet.MDMOperationTypeRemove, p.OperationType)
- existing, ok := cmdUUIDByProfileIDRemove[p.ProfileID]
+ existing, ok := cmdUUIDByProfileUUIDRemove[p.ProfileUUID]
if ok {
require.Equal(t, existing, p.CommandUUID)
} else {
- cmdUUIDByProfileIDRemove[p.ProfileID] = p.CommandUUID
+ cmdUUIDByProfileUUIDRemove[p.ProfileUUID] = p.CommandUUID
}
}
@@ -2176,42 +2173,42 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
require.ElementsMatch(t, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: p1,
ProfileIdentifier: "com.add.profile",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryPending,
},
{
- ProfileID: 2,
+ ProfileUUID: p2,
ProfileIdentifier: "com.add.profile.two",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryPending,
},
{
- ProfileID: 2,
+ ProfileUUID: p2,
ProfileIdentifier: "com.add.profile.two",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryPending,
},
{
- ProfileID: 3,
+ ProfileUUID: p3,
ProfileIdentifier: "com.remove.profile",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeRemove,
Status: &fleet.MDMDeliveryPending,
},
{
- ProfileID: 3,
+ ProfileUUID: p3,
ProfileIdentifier: "com.remove.profile",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeRemove,
Status: &fleet.MDMDeliveryPending,
},
{
- ProfileID: 4,
+ ProfileUUID: p4,
ProfileIdentifier: "com.add.profile.four",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeInstall,
@@ -2269,7 +2266,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
require.Len(t, payload, 2) // the 2 remove ops
require.ElementsMatch(t, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 3,
+ ProfileUUID: p3,
ProfileIdentifier: "com.remove.profile",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeRemove,
@@ -2277,7 +2274,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
CommandUUID: "",
},
{
- ProfileID: 3,
+ ProfileUUID: p3,
ProfileIdentifier: "com.remove.profile",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeRemove,
@@ -2306,7 +2303,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
require.Len(t, payload, 4) // the 4 install ops
require.ElementsMatch(t, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: 1,
+ ProfileUUID: p1,
ProfileIdentifier: "com.add.profile",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeInstall,
@@ -2314,7 +2311,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
CommandUUID: "",
},
{
- ProfileID: 2,
+ ProfileUUID: p2,
ProfileIdentifier: "com.add.profile.two",
HostUUID: hostUUID,
OperationType: fleet.MDMOperationTypeInstall,
@@ -2322,7 +2319,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
CommandUUID: "",
},
{
- ProfileID: 2,
+ ProfileUUID: p2,
ProfileIdentifier: "com.add.profile.two",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeInstall,
@@ -2330,7 +2327,7 @@ func TestMDMAppleReconcileAppleProfiles(t *testing.T) {
CommandUUID: "",
},
{
- ProfileID: 4,
+ ProfileUUID: p4,
ProfileIdentifier: "com.add.profile.four",
HostUUID: hostUUID2,
OperationType: fleet.MDMOperationTypeInstall,
diff --git a/server/service/client_profiles.go b/server/service/client_profiles.go
index cf7a02739c..360adef2d7 100644
--- a/server/service/client_profiles.go
+++ b/server/service/client_profiles.go
@@ -14,6 +14,9 @@ import (
"github.com/fleetdm/fleet/v4/server/fleet"
)
+// TODO(mna): those methods are unused except for an internal tool, remove or
+// migrate to new endpoints (those apple-specific endpoints are deprecated)?
+
func (c *Client) DeleteProfile(profileID uint) error {
verb, path := "DELETE", "/api/latest/fleet/mdm/apple/profiles/"+strconv.FormatUint(uint64(profileID), 10)
var responseBody deleteMDMAppleConfigProfileResponse
diff --git a/server/service/handler.go b/server/service/handler.go
index 38fd6c37ce..fee67a10d3 100644
--- a/server/service/handler.go
+++ b/server/service/handler.go
@@ -539,8 +539,8 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
mdmAnyMW.GET("/api/_version_/fleet/mdm/profiles/summary", getMDMProfilesSummaryEndpoint, getMDMProfilesSummaryRequest{})
mdmAnyMW.POST("/api/_version_/fleet/mdm/profiles", newMDMConfigProfileEndpoint, newMDMConfigProfileRequest{})
- mdmAnyMW.GET("/api/_version_/fleet/mdm/profiles/{profile_id_or_uuid}", getMDMConfigProfileEndpoint, getMDMConfigProfileRequest{})
- mdmAnyMW.DELETE("/api/_version_/fleet/mdm/profiles/{profile_id_or_uuid}", deleteMDMConfigProfileEndpoint, deleteMDMConfigProfileRequest{})
+ mdmAnyMW.GET("/api/_version_/fleet/mdm/profiles/{profile_uuid}", getMDMConfigProfileEndpoint, getMDMConfigProfileRequest{})
+ mdmAnyMW.DELETE("/api/_version_/fleet/mdm/profiles/{profile_uuid}", deleteMDMConfigProfileEndpoint, deleteMDMConfigProfileRequest{})
mdmAnyMW.GET("/api/_version_/fleet/mdm/profiles", listMDMConfigProfilesEndpoint, listMDMConfigProfilesRequest{})
// the following set of mdm endpoints must always be accessible (even
diff --git a/server/service/hosts.go b/server/service/hosts.go
index e2e992baf9..9579d1ae4e 100644
--- a/server/service/hosts.go
+++ b/server/service/hosts.go
@@ -706,7 +706,7 @@ func (svc *Service) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs []
return err
}
if !skipBulkPending {
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
}
@@ -843,7 +843,7 @@ func (svc *Service) AddHostsToTeamByFilter(ctx context.Context, teamID *uint, op
if err := svc.ds.AddHostsToTeam(ctx, teamID, hostIDs); err != nil {
return err
}
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, hostIDs, nil, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
serials, err := svc.ds.ListMDMAppleDEPSerialsInHostIDs(ctx, hostIDs)
diff --git a/server/service/hosts_test.go b/server/service/hosts_test.go
index 1e674e6227..add9157dc8 100644
--- a/server/service/hosts_test.go
+++ b/server/service/hosts_test.go
@@ -567,7 +567,7 @@ func TestHostAuth(t *testing.T) {
}
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
ds.ListMDMAppleDEPSerialsInHostIDsFunc = func(ctx context.Context, hids []uint) ([]string, error) {
@@ -790,7 +790,7 @@ func TestAddHostsToTeamByFilter(t *testing.T) {
assert.Equal(t, expectedHostIDs, hostIDs)
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
ds.ListMDMAppleDEPSerialsInHostIDsFunc = func(ctx context.Context, hids []uint) ([]string, error) {
@@ -825,7 +825,7 @@ func TestAddHostsToTeamByFilterLabel(t *testing.T) {
assert.Equal(t, expectedHostIDs, hostIDs)
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
ds.ListMDMAppleDEPSerialsInHostIDsFunc = func(ctx context.Context, hids []uint) ([]string, error) {
@@ -853,7 +853,7 @@ func TestAddHostsToTeamByFilterEmptyHosts(t *testing.T) {
ds.AddHostsToTeamFunc = func(ctx context.Context, teamID *uint, hostIDs []uint) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go
index 5162e05d39..49b46d85b6 100644
--- a/server/service/integration_mdm_test.go
+++ b/server/service/integration_mdm_test.go
@@ -2825,7 +2825,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
hostCmdUUID := uuid.New().String()
err = s.ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: fileVaultProf.ProfileID,
+ ProfileUUID: fileVaultProf.ProfileUUID,
ProfileIdentifier: fileVaultProf.Identifier,
HostUUID: host.UUID,
CommandUUID: hostCmdUUID,
@@ -2840,13 +2840,13 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
err := s.ds.UpdateOrDeleteHostMDMAppleProfile(ctx, &fleet.HostMDMAppleProfile{
HostUUID: host.UUID,
CommandUUID: hostCmdUUID,
- ProfileID: fileVaultProf.ProfileID,
+ ProfileUUID: fileVaultProf.ProfileUUID,
Status: &fleet.MDMDeliveryVerifying,
OperationType: fleet.MDMOperationTypeRemove,
})
require.NoError(t, err)
// not an error if the profile does not exist
- _ = s.ds.DeleteMDMAppleConfigProfile(ctx, fileVaultProf.ProfileID)
+ _ = s.ds.DeleteMDMAppleConfigProfile(ctx, fileVaultProf.ProfileUUID)
})
// get that host - it should
@@ -2865,7 +2865,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
err = s.ds.UpdateOrDeleteHostMDMAppleProfile(ctx, &fleet.HostMDMAppleProfile{
HostUUID: host.UUID,
CommandUUID: hostCmdUUID,
- ProfileID: fileVaultProf.ProfileID,
+ ProfileUUID: fileVaultProf.ProfileUUID,
Status: &fleet.MDMDeliveryFailed,
OperationType: fleet.MDMOperationTypeInstall,
Detail: "test error",
@@ -2887,7 +2887,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
err = s.ds.UpdateOrDeleteHostMDMAppleProfile(ctx, &fleet.HostMDMAppleProfile{
HostUUID: host.UUID,
CommandUUID: hostCmdUUID,
- ProfileID: fileVaultProf.ProfileID,
+ ProfileUUID: fileVaultProf.ProfileUUID,
Status: &fleet.MDMDeliveryVerified,
OperationType: fleet.MDMOperationTypeInstall,
Detail: "",
@@ -3615,7 +3615,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleDiskEncryptionAggregate() {
hostCmdUUID := uuid.New().String()
err := s.ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
- ProfileID: prof.ProfileID,
+ ProfileUUID: prof.ProfileUUID,
ProfileIdentifier: prof.Identifier,
HostUUID: host.UUID,
CommandUUID: hostCmdUUID,
@@ -8338,11 +8338,12 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
var resp newMDMConfigProfileResponse
err := json.NewDecoder(res.Body).Decode(&resp)
require.NoError(t, err)
- require.NotEmpty(t, resp.ProfileID)
- return resp.ProfileID
+ require.NotEmpty(t, resp.ProfileUUID)
+ require.Equal(t, "a", string(resp.ProfileUUID[0]))
+ return resp.ProfileUUID
}
createAppleProfile := func(name, ident string, teamID uint) string {
- id := assertAppleProfile(name+".mobileconfig", name, ident, teamID, http.StatusOK, "")
+ uid := assertAppleProfile(name+".mobileconfig", name, ident, teamID, http.StatusOK, "")
var wantJSON string
if teamID == 0 {
@@ -8352,7 +8353,7 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
}
s.lastActivityOfTypeMatches(fleet.ActivityTypeCreatedMacosProfile{}.ActivityName(), wantJSON, 0)
- return id
+ return uid
}
assertWindowsProfile := func(filename, locURI string, teamID uint, wantStatus int, wantErrMsg string) string {
@@ -8373,11 +8374,12 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
var resp newMDMConfigProfileResponse
err := json.NewDecoder(res.Body).Decode(&resp)
require.NoError(t, err)
- require.NotEmpty(t, resp.ProfileID)
- return resp.ProfileID
+ require.NotEmpty(t, resp.ProfileUUID)
+ require.Equal(t, "w", string(resp.ProfileUUID[0]))
+ return resp.ProfileUUID
}
createWindowsProfile := func(name string, teamID uint) string {
- id := assertWindowsProfile(name+".xml", "./Test", teamID, http.StatusOK, "")
+ uid := assertWindowsProfile(name+".xml", "./Test", teamID, http.StatusOK, "")
var wantJSON string
if teamID == 0 {
@@ -8387,15 +8389,15 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
}
s.lastActivityOfTypeMatches(fleet.ActivityTypeCreatedWindowsProfile{}.ActivityName(), wantJSON, 0)
- return id
+ return uid
}
// create a couple Apple profiles for no-team and team
- noTeamAppleProfID := createAppleProfile("apple-global-profile", "test-global-ident", 0)
- teamAppleProfID := createAppleProfile("apple-team-profile", "test-team-ident", testTeam.ID)
+ noTeamAppleProfUUID := createAppleProfile("apple-global-profile", "test-global-ident", 0)
+ teamAppleProfUUID := createAppleProfile("apple-team-profile", "test-team-ident", testTeam.ID)
// create a couple Windows profiles for no-team and team
- noTeamWinProfID := createWindowsProfile("win-global-profile", 0)
- teamWinProfID := createWindowsProfile("win-team-profile", testTeam.ID)
+ noTeamWinProfUUID := createWindowsProfile("win-global-profile", 0)
+ teamWinProfUUID := createWindowsProfile("win-team-profile", testTeam.ID)
// Windows profile name conflicts with Apple's for no team
assertWindowsProfile("apple-global-profile.xml", "./Test", 0, http.StatusConflict, "Couldn't upload. A configuration profile with this name already exists.")
@@ -8443,14 +8445,14 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
// get the existing profiles work
expectedProfiles := []fleet.MDMConfigProfilePayload{
- {ProfileID: fmt.Sprint(noTeamAppleProfID), Platform: "darwin", Name: "apple-global-profile", Identifier: "test-global-ident", TeamID: nil},
- {ProfileID: fmt.Sprint(teamAppleProfID), Platform: "darwin", Name: "apple-team-profile", Identifier: "test-team-ident", TeamID: &testTeam.ID},
- {ProfileID: noTeamWinProfID, Platform: "windows", Name: "win-global-profile", TeamID: nil},
- {ProfileID: teamWinProfID, Platform: "windows", Name: "win-team-profile", TeamID: &testTeam.ID},
+ {ProfileUUID: noTeamAppleProfUUID, Platform: "darwin", Name: "apple-global-profile", Identifier: "test-global-ident", TeamID: nil},
+ {ProfileUUID: teamAppleProfUUID, Platform: "darwin", Name: "apple-team-profile", Identifier: "test-team-ident", TeamID: &testTeam.ID},
+ {ProfileUUID: noTeamWinProfUUID, Platform: "windows", Name: "win-global-profile", TeamID: nil},
+ {ProfileUUID: teamWinProfUUID, Platform: "windows", Name: "win-team-profile", TeamID: &testTeam.ID},
}
for _, prof := range expectedProfiles {
var getResp getMDMConfigProfileResponse
- s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", prof.ProfileID), nil, http.StatusOK, &getResp)
+ s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", prof.ProfileUUID), nil, http.StatusOK, &getResp)
require.NotZero(t, getResp.CreatedAt)
require.NotZero(t, getResp.UpdatedAt)
if getResp.Platform == "darwin" {
@@ -8462,7 +8464,7 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
getResp.Checksum = nil
require.Equal(t, prof, *getResp.MDMConfigProfilePayload)
- resp := s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", prof.ProfileID), nil, http.StatusOK, "alt", "media")
+ resp := s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", prof.ProfileUUID), nil, http.StatusOK, "alt", "media")
require.NotZero(t, resp.ContentLength)
require.Contains(t, resp.Header.Get("Content-Disposition"), "attachment;")
if getResp.Platform == "darwin" {
@@ -8479,49 +8481,45 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
var getResp getMDMConfigProfileResponse
// get an unknown Apple profile
- s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%d", 99999), nil, http.StatusNotFound, &getResp)
- s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%d", 99999), nil, http.StatusNotFound, "alt", "media")
+ s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "ano-such-profile"), nil, http.StatusNotFound, &getResp)
+ s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "ano-such-profile"), nil, http.StatusNotFound, "alt", "media")
// get an unknown Windows profile
- s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "no-such-profile"), nil, http.StatusNotFound, &getResp)
- s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "no-such-profile"), nil, http.StatusNotFound, "alt", "media")
+ s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "wno-such-profile"), nil, http.StatusNotFound, &getResp)
+ s.Do("GET", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "wno-such-profile"), nil, http.StatusNotFound, "alt", "media")
var deleteResp deleteMDMConfigProfileResponse
// delete existing Apple profiles
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", noTeamAppleProfID), nil, http.StatusOK, &deleteResp)
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", teamAppleProfID), nil, http.StatusOK, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", noTeamAppleProfUUID), nil, http.StatusOK, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", teamAppleProfUUID), nil, http.StatusOK, &deleteResp)
// delete non-existing Apple profile
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%d", 99999), nil, http.StatusNotFound, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "ano-such-profile"), nil, http.StatusNotFound, &deleteResp)
// delete existing Windows profiles
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", noTeamWinProfID), nil, http.StatusOK, &deleteResp)
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", teamWinProfID), nil, http.StatusOK, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", noTeamWinProfUUID), nil, http.StatusOK, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", teamWinProfUUID), nil, http.StatusOK, &deleteResp)
// delete non-existing Windows profile
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "no-such-profile"), nil, http.StatusNotFound, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", "wno-such-profile"), nil, http.StatusNotFound, &deleteResp)
// trying to create/delete profiles managed by Fleet fails
for p := range mobileconfig.FleetPayloadIdentifiers() {
assertAppleProfile("foo.mobileconfig", p, p, 0, http.StatusBadRequest, fmt.Sprintf("payload identifier %s is not allowed", p))
// create it directly in the DB to test deletion
- var id int64
+ uid := "a" + uuid.NewString()
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
mc := mcBytesForTest(p, p, uuid.New().String())
- res, err := q.ExecContext(ctx,
- "INSERT INTO mdm_apple_configuration_profiles (identifier, name, mobileconfig, checksum, team_id) VALUES (?, ?, ?, ?, ?)",
- p, p, mc, "1234", 0)
- if err != nil {
- return err
- }
- id, _ = res.LastInsertId()
- return nil
+ _, err := q.ExecContext(ctx,
+ "INSERT INTO mdm_apple_configuration_profiles (profile_uuid, identifier, name, mobileconfig, checksum, team_id) VALUES (?, ?, ?, ?, ?, ?)",
+ uid, p, p, mc, "1234", 0)
+ return err
})
var deleteResp deleteMDMConfigProfileResponse
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%d", id), nil, http.StatusBadRequest, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", uid), nil, http.StatusBadRequest, &deleteResp)
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx,
- "DELETE FROM mdm_apple_configuration_profiles WHERE profile_id = ?",
- id)
+ "DELETE FROM mdm_apple_configuration_profiles WHERE profile_uuid = ?",
+ uid)
return err
})
}
@@ -8535,7 +8533,7 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
profile := s.assertConfigProfilesByIdentifier(nil, mobileconfig.FleetFileVaultPayloadIdentifier, true)
// try to delete the profile
- s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%d", profile.ProfileID), nil, http.StatusBadRequest, &deleteResp)
+ s.DoJSON("DELETE", fmt.Sprintf("/api/latest/fleet/mdm/profiles/%s", profile.ProfileUUID), nil, http.StatusBadRequest, &deleteResp)
// make fleet add a Windows OS Updates profile
acResp = appConfigResponse{}
@@ -8607,18 +8605,18 @@ func (s *integrationMDMTestSuite) TestListMDMConfigProfiles() {
listResp.Profiles[0].CreatedAt, listResp.Profiles[0].UpdatedAt = time.Time{}, time.Time{}
listResp.Profiles[1].CreatedAt, listResp.Profiles[1].UpdatedAt = time.Time{}, time.Time{}
require.Equal(t, &fleet.MDMConfigProfilePayload{
- ProfileID: fmt.Sprint(tm2ProfF.ProfileID),
- TeamID: tm2ProfF.TeamID,
- Name: tm2ProfF.Name,
- Platform: "darwin",
- Identifier: tm2ProfF.Identifier,
- Checksum: tm2ProfF.Checksum,
+ ProfileUUID: tm2ProfF.ProfileUUID,
+ TeamID: tm2ProfF.TeamID,
+ Name: tm2ProfF.Name,
+ Platform: "darwin",
+ Identifier: tm2ProfF.Identifier,
+ Checksum: tm2ProfF.Checksum,
}, listResp.Profiles[0])
require.Equal(t, &fleet.MDMConfigProfilePayload{
- ProfileID: tm2ProfG.ProfileUUID,
- TeamID: tm2ProfG.TeamID,
- Name: tm2ProfG.Name,
- Platform: "windows",
+ ProfileUUID: tm2ProfG.ProfileUUID,
+ TeamID: tm2ProfG.TeamID,
+ Name: tm2ProfG.Name,
+ Platform: "windows",
}, listResp.Profiles[1])
// list for a non-existing team returns 404
diff --git a/server/service/mdm.go b/server/service/mdm.go
index 21285f3a70..248eb624af 100644
--- a/server/service/mdm.go
+++ b/server/service/mdm.go
@@ -978,12 +978,12 @@ func (svc *Service) authorizeAllHostsTeams(ctx context.Context, hostUUIDs []stri
}
////////////////////////////////////////////////////////////////////////////////
-// GET /mdm/profiles/{id_or_uuid}
+// GET /mdm/profiles/{uuid}
////////////////////////////////////////////////////////////////////////////////
type getMDMConfigProfileRequest struct {
- ProfileIDOrUUID string `url:"profile_id_or_uuid"`
- Alt string `query:"alt,optional"`
+ ProfileUUID string `url:"profile_uuid"`
+ Alt string `query:"alt,optional"`
}
type getMDMConfigProfileResponse struct {
@@ -997,11 +997,10 @@ func getMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
req := request.(*getMDMConfigProfileRequest)
downloadRequested := req.Alt == "media"
- appleID, isApple := isAppleProfileID(req.ProfileIDOrUUID)
var err error
- if isApple {
+ if isAppleProfileUUID(req.ProfileUUID) {
// Apple config profile
- cp, err := svc.GetMDMAppleConfigProfile(ctx, appleID)
+ cp, err := svc.GetMDMAppleConfigProfile(ctx, req.ProfileUUID)
if err != nil {
return &getMDMConfigProfileResponse{Err: err}, nil
}
@@ -1019,7 +1018,7 @@ func getMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
}
// Windows config profile
- cp, err := svc.GetMDMWindowsConfigProfile(ctx, req.ProfileIDOrUUID)
+ cp, err := svc.GetMDMWindowsConfigProfile(ctx, req.ProfileUUID)
if err != nil {
return &getMDMConfigProfileResponse{Err: err}, nil
}
@@ -1057,11 +1056,11 @@ func (svc *Service) GetMDMWindowsConfigProfile(ctx context.Context, profileUUID
}
////////////////////////////////////////////////////////////////////////////////
-// DELETE /mdm/profiles/{id_or_uuid}
+// DELETE /mdm/profiles/{uuid}
////////////////////////////////////////////////////////////////////////////////
type deleteMDMConfigProfileRequest struct {
- ProfileIDOrUUID string `url:"profile_id_or_uuid"`
+ ProfileUUID string `url:"profile_uuid"`
}
type deleteMDMConfigProfileResponse struct {
@@ -1073,12 +1072,11 @@ func (r deleteMDMConfigProfileResponse) error() error { return r.Err }
func deleteMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
req := request.(*deleteMDMConfigProfileRequest)
- appleID, isApple := isAppleProfileID(req.ProfileIDOrUUID)
var err error
- if isApple {
- err = svc.DeleteMDMAppleConfigProfile(ctx, appleID)
+ if isAppleProfileUUID(req.ProfileUUID) {
+ err = svc.DeleteMDMAppleConfigProfile(ctx, req.ProfileUUID)
} else {
- err = svc.DeleteMDMWindowsConfigProfile(ctx, req.ProfileIDOrUUID)
+ err = svc.DeleteMDMWindowsConfigProfile(ctx, req.ProfileUUID)
}
return &deleteMDMConfigProfileResponse{Err: err}, nil
}
@@ -1092,7 +1090,7 @@ func (svc *Service) DeleteMDMWindowsConfigProfile(ctx context.Context, profileUU
// check that Windows MDM is enabled - the middleware of that endpoint checks
// only that any MDM is enabled, maybe it's just macOS
if err := svc.VerifyMDMWindowsConfigured(ctx); err != nil {
- err := fleet.NewInvalidArgumentError("profile_id", fleet.WindowsMDMNotConfiguredMessage).WithStatus(http.StatusBadRequest)
+ err := fleet.NewInvalidArgumentError("profile_uuid", fleet.WindowsMDMNotConfiguredMessage).WithStatus(http.StatusBadRequest)
return ctxerr.Wrap(ctx, err, "check windows MDM enabled")
}
@@ -1128,7 +1126,7 @@ func (svc *Service) DeleteMDMWindowsConfigProfile(ctx context.Context, profileUU
}
// cannot use the profile ID as it is now deleted
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{teamID}, nil, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, []uint{teamID}, nil, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
@@ -1153,14 +1151,11 @@ func (svc *Service) DeleteMDMWindowsConfigProfile(ctx context.Context, profileUU
// returns the numeric Apple profile ID and true if it is an Apple identifier,
// or 0 and false otherwise.
-func isAppleProfileID(profileIDOrUUID string) (uint, bool) {
- // parsing as 32 bits as that's the maximum value of the DB column (and can
- // be safely converted to uint).
- id, err := strconv.ParseUint(profileIDOrUUID, 10, 32)
- if err != nil {
- return 0, false
+func isAppleProfileUUID(profileUUID string) bool {
+ if strings.HasPrefix(profileUUID, "a") {
+ return true
}
- return uint(id), true
+ return false
}
////////////////////////////////////////////////////////////////////////////////
@@ -1205,8 +1200,8 @@ func (newMDMConfigProfileRequest) DecodeRequest(ctx context.Context, r *http.Req
}
type newMDMConfigProfileResponse struct {
- ProfileID string `json:"profile_id"`
- Err error `json:"error,omitempty"`
+ ProfileUUID string `json:"profile_uuid"`
+ Err error `json:"error,omitempty"`
}
func (r newMDMConfigProfileResponse) error() error { return r.Err }
@@ -1227,7 +1222,7 @@ func newMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
return &newMDMConfigProfileResponse{Err: err}, nil
}
return &newMDMConfigProfileResponse{
- ProfileID: fmt.Sprint(cp.ProfileID),
+ ProfileUUID: cp.ProfileUUID,
}, nil
}
@@ -1238,7 +1233,7 @@ func newMDMConfigProfileEndpoint(ctx context.Context, request interface{}, svc f
return &newMDMConfigProfileResponse{Err: err}, nil
}
return &newMDMConfigProfileResponse{
- ProfileID: cp.ProfileUUID,
+ ProfileUUID: cp.ProfileUUID,
}, nil
}
@@ -1312,7 +1307,7 @@ func (svc *Service) NewMDMWindowsConfigProfile(ctx context.Context, teamID uint,
return nil, ctxerr.Wrap(ctx, err)
}
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, []string{newCP.ProfileUUID}, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, []string{newCP.ProfileUUID}, nil); err != nil {
return nil, ctxerr.Wrap(ctx, err, "bulk set pending host profiles")
}
@@ -1400,16 +1395,16 @@ func (svc *Service) BatchSetMDMProfiles(ctx context.Context, tmID *uint, tmName
for _, p := range windowsProfiles {
winProfUUIDs = append(winProfUUIDs, p.ProfileUUID)
}
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, nil, winProfUUIDs, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, winProfUUIDs, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending windows host profiles")
}
// set pending status for apple profiles
- appleProfIDs := []uint{}
+ appleProfUUIDs := []string{}
for _, p := range appleProfiles {
- appleProfIDs = append(appleProfIDs, p.ProfileID)
+ appleProfUUIDs = append(appleProfUUIDs, p.ProfileUUID)
}
- if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, appleProfIDs, nil, nil); err != nil {
+ if err := svc.ds.BulkSetPendingMDMHostProfiles(ctx, nil, nil, appleProfUUIDs, nil); err != nil {
return ctxerr.Wrap(ctx, err, "bulk set pending apple host profiles")
}
diff --git a/server/service/mdm_test.go b/server/service/mdm_test.go
index f65e55028e..3d30e02e15 100644
--- a/server/service/mdm_test.go
+++ b/server/service/mdm_test.go
@@ -936,7 +936,7 @@ func TestMDMWindowsConfigProfileAuthz(t *testing.T) {
ds.ListMDMConfigProfilesFunc = func(ctx context.Context, teamID *uint, opt fleet.ListOptions) ([]*fleet.MDMConfigProfilePayload, *fleet.PaginationMetadata, error) {
return nil, nil, nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
return nil
}
@@ -1010,7 +1010,7 @@ func TestUploadWindowsMDMConfigProfileValidations(t *testing.T) {
cp.ProfileUUID = uuid.New().String()
return &cp, nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
return nil
}
@@ -1099,7 +1099,7 @@ func TestMDMBatchSetProfiles(t *testing.T) {
ds.NewActivityFunc = func(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hostIDs []uint, teamIDs []uint, profileUUIDs []string, hostUUIDs []string) error {
return nil
}
diff --git a/server/service/microsoft_mdm.go b/server/service/microsoft_mdm.go
index 2b38dd66d6..85a0a0dcd9 100644
--- a/server/service/microsoft_mdm.go
+++ b/server/service/microsoft_mdm.go
@@ -2098,7 +2098,7 @@ func ReconcileWindowsProfiles(ctx context.Context, ds fleet.Datastore, logger ki
// with the new status, operation_type, etc.
hostProfiles := make([]*fleet.MDMWindowsBulkUpsertHostProfilePayload, 0, len(toInstall))
- // install are maps from profileID -> command uuid and host
+ // install are maps from profileUUID -> command uuid and host
// UUIDs as the underlying MDM services are optimized to send one command to
// multiple hosts at the same time. Note that the same command uuid is used
// for all hosts in a given install/remove target operation.
@@ -2129,7 +2129,7 @@ func ReconcileWindowsProfiles(ctx context.Context, ds fleet.Datastore, logger ki
OperationType: fleet.MDMOperationTypeInstall,
Status: &fleet.MDMDeliveryPending,
})
- level.Debug(logger).Log("msg", "installing profile", "profile_id", p.ProfileUUID, "host_id", p.HostUUID, "name", p.ProfileName)
+ level.Debug(logger).Log("msg", "installing profile", "profile_uuid", p.ProfileUUID, "host_id", p.HostUUID, "name", p.ProfileName)
}
// Grab the contents of all the profiles we need to install
@@ -2142,16 +2142,16 @@ func ReconcileWindowsProfiles(ctx context.Context, ds fleet.Datastore, logger ki
return ctxerr.Wrap(ctx, err, "get profile contents")
}
- for profID, target := range installTargets {
- p, ok := profileContents[profID]
+ for profUUID, target := range installTargets {
+ p, ok := profileContents[profUUID]
if !ok {
// this should never happen
- return ctxerr.Wrap(ctx, err, "inserting commands for hosts")
+ return ctxerr.Wrapf(ctx, err, "missing profile content for profile %s", profUUID)
}
command, err := buildCommandFromProfileBytes(p, target.cmdUUID)
if err != nil {
- level.Info(logger).Log("err", err, "profile_id", profID)
+ level.Info(logger).Log("err", err, "profile_uuid", profUUID)
continue
}
if err := ds.MDMWindowsInsertCommandForHosts(ctx, target.hostUUIDs, command); err != nil {
diff --git a/server/service/teams_test.go b/server/service/teams_test.go
index f240514970..8177890ff0 100644
--- a/server/service/teams_test.go
+++ b/server/service/teams_test.go
@@ -50,7 +50,7 @@ func TestTeamAuth(t *testing.T) {
ds.ApplyEnrollSecretsFunc = func(ctx context.Context, teamID *uint, secrets []*fleet.EnrollSecret) error {
return nil
}
- ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids, pids []uint, puuids, uuids []string) error {
+ ds.BulkSetPendingMDMHostProfilesFunc = func(ctx context.Context, hids, tids []uint, puuids, uuids []string) error {
return nil
}
ds.ListHostsFunc = func(ctx context.Context, filter fleet.TeamFilter, opt fleet.HostListOptions) ([]*fleet.Host, error) {
From 0db59a7a2575c2a92691c0c3a75a4448e95d0ef0 Mon Sep 17 00:00:00 2001
From: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com>
Date: Mon, 4 Dec 2023 10:09:23 -0600
Subject: [PATCH 06/12] Add cron for software titles feature (#15422)
---
cmd/fleet/cron.go | 6 +
server/datastore/mysql/software.go | 60 +++++++
server/datastore/mysql/software_test.go | 200 ++++++++++++++++++++++++
server/fleet/datastore.go | 7 +
server/fleet/software.go | 8 +
server/mock/datastore_mock.go | 12 ++
6 files changed, 293 insertions(+)
diff --git a/cmd/fleet/cron.go b/cmd/fleet/cron.go
index 65eb427f9d..725af151f7 100644
--- a/cmd/fleet/cron.go
+++ b/cmd/fleet/cron.go
@@ -71,6 +71,12 @@ func newVulnerabilitiesSchedule(
return ds.SyncHostsSoftware(ctx, time.Now())
},
),
+ schedule.WithJob(
+ "cron_reconcile_software_titles",
+ func(ctx context.Context) error {
+ return ds.ReconcileSoftwareTitles(ctx)
+ },
+ ),
)
return s, nil
diff --git a/server/datastore/mysql/software.go b/server/datastore/mysql/software.go
index 9aa0cfd8b1..18f67b0c5e 100644
--- a/server/datastore/mysql/software.go
+++ b/server/datastore/mysql/software.go
@@ -13,6 +13,7 @@ import (
_ "github.com/doug-martin/goqu/v9/dialect/mysql"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
+ "github.com/go-kit/kit/log/level"
"github.com/jmoiron/sqlx"
)
@@ -1286,6 +1287,65 @@ func (ds *Datastore) SyncHostsSoftware(ctx context.Context, updatedAt time.Time)
return nil
}
+func (ds *Datastore) ReconcileSoftwareTitles(ctx context.Context) error {
+ // TODO: consider if we should batch writes to software or software_titles table
+
+ // ensure all software titles are in the software_titles table
+ upsertTitlesStmt := `
+INSERT INTO software_titles (name, source)
+SELECT DISTINCT
+ name,
+ source
+FROM
+ software s
+WHERE
+ NOT EXISTS (SELECT 1 FROM software_titles st WHERE (s.name, s.source) = (st.name, st.source))
+ON DUPLICATE KEY UPDATE software_titles.id = software_titles.id`
+ // TODO: consider the impact of on duplicate key update vs. risk of insert ignore
+ // or performing a select first to see if the title exists and only inserting
+ // new titles
+
+ res, err := ds.writer(ctx).ExecContext(ctx, upsertTitlesStmt)
+ if err != nil {
+ return ctxerr.Wrap(ctx, err, "upsert software titles")
+ }
+ n, _ := res.RowsAffected()
+ level.Debug(ds.logger).Log("msg", "upsert software titles", "rows_affected", n)
+
+ // update title ids for software table entries
+ updateSoftwareStmt := `
+UPDATE
+ software s,
+ software_titles st
+SET
+ s.title_id = st.id
+WHERE
+ (s.name, s.source) = (st.name, st.source)
+ AND (s.title_id IS NULL OR s.title_id != st.id)`
+
+ res, err = ds.writer(ctx).ExecContext(ctx, updateSoftwareStmt)
+ if err != nil {
+ return ctxerr.Wrap(ctx, err, "update software titles")
+ }
+ n, _ = res.RowsAffected()
+ level.Debug(ds.logger).Log("msg", "update software titles", "rows_affected", n)
+
+ // clean up orphaned software titles
+ cleanupStmt := `
+DELETE st FROM software_titles st
+ LEFT JOIN software s ON s.title_id = st.id
+ WHERE s.title_id IS NULL`
+
+ res, err = ds.writer(ctx).ExecContext(ctx, cleanupStmt)
+ if err != nil {
+ return ctxerr.Wrap(ctx, err, "cleanup orphaned software titles")
+ }
+ n, _ = res.RowsAffected()
+ level.Debug(ds.logger).Log("msg", "cleanup orphaned software titles", "rows_affected", n)
+
+ return nil
+}
+
func (ds *Datastore) HostVulnSummariesBySoftwareIDs(ctx context.Context, softwareIDs []uint) ([]fleet.HostVulnerabilitySummary, error) {
stmt := `
SELECT DISTINCT
diff --git a/server/datastore/mysql/software_test.go b/server/datastore/mysql/software_test.go
index e159c31907..7ebe61a2bb 100644
--- a/server/datastore/mysql/software_test.go
+++ b/server/datastore/mysql/software_test.go
@@ -2578,3 +2578,203 @@ func testInsertHostSoftwareInstalledPaths(t *testing.T, ds *Datastore) {
require.ElementsMatch(t, actual, toInsert)
}
+
+func TestReconcileSoftwareTitles(t *testing.T) {
+ ds := CreateMySQLDS(t)
+ ctx := context.Background()
+
+ host1 := test.NewHost(t, ds, "host1", "", "host1key", "host1uuid", time.Now())
+ host2 := test.NewHost(t, ds, "host2", "", "host2key", "host2uuid", time.Now())
+ host3 := test.NewHost(t, ds, "host3", "", "host3key", "host3uuid", time.Now())
+
+ expectedSoftware := []fleet.Software{
+ {Name: "foo", Version: "0.0.1", Source: "chrome_extensions"},
+ {Name: "foo", Version: "v0.0.2", Source: "chrome_extensions"},
+ {Name: "foo", Version: "0.0.3", Source: "chrome_extensions"},
+ {Name: "bar", Version: "0.0.3", Source: "deb_packages"},
+ {Name: "baz", Version: "0.0.1", Source: "deb_packages"},
+ }
+
+ software1 := []fleet.Software{expectedSoftware[0], expectedSoftware[2]}
+ software2 := []fleet.Software{expectedSoftware[1], expectedSoftware[2], expectedSoftware[3]}
+ software3 := []fleet.Software{expectedSoftware[4]}
+
+ _, err := ds.UpdateHostSoftware(ctx, host1.ID, software1)
+ require.NoError(t, err)
+ _, err = ds.UpdateHostSoftware(ctx, host2.ID, software2)
+ require.NoError(t, err)
+ _, err = ds.UpdateHostSoftware(ctx, host3.ID, software3)
+ require.NoError(t, err)
+
+ getSoftware := func() ([]fleet.Software, error) {
+ var sw []fleet.Software
+ err := ds.writer(ctx).SelectContext(ctx, &sw, `SELECT * FROM software ORDER BY name, version`)
+ if err != nil {
+ return nil, err
+ }
+ return sw, nil
+ }
+
+ getTitles := func() ([]fleet.SoftwareTitle, error) {
+ var swt []fleet.SoftwareTitle
+ err := ds.writer(ctx).SelectContext(ctx, &swt, `SELECT * FROM software_titles ORDER BY name, source`)
+ if err != nil {
+ return nil, err
+ }
+ return swt, nil
+ }
+
+ expectedTitlesByNS := map[string]fleet.SoftwareTitle{}
+ assertSoftware := func(t *testing.T, wantSoftware []fleet.Software, wantNilTitleID []fleet.Software) {
+ gotSoftware, err := getSoftware()
+ require.NoError(t, err)
+ require.Len(t, gotSoftware, len(wantSoftware))
+
+ byNSV := map[string]fleet.Software{}
+ for _, s := range wantSoftware {
+ byNSV[s.Name+s.Source+s.Version] = s
+ }
+
+ for _, r := range gotSoftware {
+ _, ok := byNSV[r.Name+r.Source+r.Version]
+ require.True(t, ok)
+
+ if r.TitleID == nil {
+ var found bool
+ for _, s := range wantNilTitleID {
+ if s.Name == r.Name && s.Source == r.Source && s.Version == r.Version {
+ found = true
+ break
+ }
+ }
+ require.True(t, found)
+ } else {
+ require.NotNil(t, r.TitleID)
+ swt, ok := expectedTitlesByNS[r.Name+r.Source]
+ require.True(t, ok)
+ require.NotNil(t, r.TitleID)
+ require.Equal(t, swt.ID, *r.TitleID)
+ require.Equal(t, swt.Name, r.Name)
+ require.Equal(t, swt.Source, r.Source)
+ }
+ }
+ }
+
+ assertTitles := func(t *testing.T, gotTitles []fleet.SoftwareTitle, expectMissing []string) {
+ for _, r := range gotTitles {
+ if len(expectMissing) > 0 {
+ require.NotContains(t, expectMissing, r.Name)
+ }
+ e, ok := expectedTitlesByNS[r.Name+r.Source]
+ require.True(t, ok)
+ require.Equal(t, e.ID, r.ID)
+ require.Equal(t, e.Name, r.Name)
+ require.Equal(t, e.Source, r.Source)
+ }
+ }
+
+ // title_id is initially nil for all software entries
+ assertSoftware(t, expectedSoftware, expectedSoftware)
+
+ // reconcile software titles
+ require.NoError(t, ds.ReconcileSoftwareTitles(ctx))
+ swt, err := getTitles()
+ require.NoError(t, err)
+ require.Len(t, swt, 3)
+
+ require.Equal(t, swt[0].Name, "bar")
+ require.Equal(t, swt[0].Source, "deb_packages")
+ expectedTitlesByNS[swt[0].Name+swt[0].Source] = swt[0]
+
+ require.Equal(t, swt[1].Name, "baz")
+ require.Equal(t, swt[1].Source, "deb_packages")
+ expectedTitlesByNS[swt[1].Name+swt[1].Source] = swt[1]
+
+ require.Equal(t, swt[2].Name, "foo")
+ require.Equal(t, swt[2].Source, "chrome_extensions")
+ expectedTitlesByNS[swt[2].Name+swt[2].Source] = swt[2]
+
+ // title_id is now populated for all software entries
+ assertSoftware(t, expectedSoftware, nil)
+
+ // remove the bar software title from host 2
+ _, err = ds.UpdateHostSoftware(context.Background(), host2.ID, software2[:2])
+ require.NoError(t, err)
+ assertSoftware(t, []fleet.Software{expectedSoftware[0], expectedSoftware[1], expectedSoftware[2], expectedSoftware[4]}, nil)
+
+ // bar is no longer associated with any host so the title should be deleted
+ require.NoError(t, ds.ReconcileSoftwareTitles(context.Background()))
+ gotTitles, err := getTitles()
+ require.NoError(t, err)
+ require.Len(t, gotTitles, 2)
+ assertTitles(t, gotTitles, []string{"bar"})
+
+ // add bar to host 3
+ _, err = ds.UpdateHostSoftware(context.Background(), host3.ID, []fleet.Software{expectedSoftware[3], expectedSoftware[4]})
+ require.NoError(t, err)
+ require.NoError(t, ds.SyncHostsSoftware(context.Background(), time.Now()))
+
+ // title_id is initially nil for new software entries
+ assertSoftware(t, expectedSoftware, []fleet.Software{expectedSoftware[3]})
+
+ // bar isn't added back to software titles until we reconcile software titles
+ gotTitles, err = getTitles()
+ require.NoError(t, err)
+ require.Len(t, gotTitles, 2)
+ assertTitles(t, gotTitles, []string{"bar"})
+
+ // reconcile software titles
+ require.NoError(t, ds.ReconcileSoftwareTitles(ctx))
+ gotTitles, err = getTitles()
+ require.NoError(t, err)
+ require.Len(t, gotTitles, 3)
+
+ // bar was added back to software titles with a new ID
+ require.Equal(t, gotTitles[0].Name, "bar")
+ require.Equal(t, gotTitles[0].Source, "deb_packages")
+ require.NotEqual(t, expectedTitlesByNS[gotTitles[0].Name+gotTitles[0].Source], gotTitles[0].ID)
+ expectedTitlesByNS[gotTitles[0].Name+gotTitles[0].Source] = gotTitles[0]
+ assertTitles(t, gotTitles, nil)
+
+ // title_id is now populated for bar
+ assertSoftware(t, expectedSoftware, nil)
+
+ // add a new version of foo to host 3
+ expectedSoftware = append(expectedSoftware, fleet.Software{Name: "foo", Version: "0.0.4", Source: "chrome_extensions"})
+ _, err = ds.UpdateHostSoftware(ctx, host3.ID, expectedSoftware[3:])
+ require.NoError(t, err)
+
+ // title_id is initially nil for new software entries
+ assertSoftware(t, expectedSoftware, []fleet.Software{expectedSoftware[5]})
+
+ // new version of foo doesn't result in a new software title entry
+ require.NoError(t, ds.ReconcileSoftwareTitles(ctx))
+ gotTitles, err = getTitles()
+ require.NoError(t, err)
+ require.Len(t, gotTitles, 3)
+ assertTitles(t, gotTitles, nil)
+
+ // title_id is now populated for new version of foo
+ assertSoftware(t, expectedSoftware, nil)
+
+ // add a new source of foo to host 3
+ expectedSoftware = append(expectedSoftware, fleet.Software{Name: "foo", Version: "0.0.4", Source: "rpm_packages"})
+ _, err = ds.UpdateHostSoftware(ctx, host3.ID, expectedSoftware[3:])
+ require.NoError(t, err)
+
+ // title_id is initially nil for new software entries
+ assertSoftware(t, expectedSoftware, []fleet.Software{expectedSoftware[6]})
+
+ // new source of foo results in a new software title entry
+ require.NoError(t, ds.ReconcileSoftwareTitles(ctx))
+ gotTitles, err = getTitles()
+ require.NoError(t, err)
+ require.Len(t, gotTitles, 4)
+ require.Equal(t, gotTitles[3].Name, "foo")
+ require.Equal(t, gotTitles[3].Source, "rpm_packages")
+ expectedTitlesByNS[gotTitles[3].Name+gotTitles[3].Source] = gotTitles[3]
+ assertTitles(t, gotTitles, nil)
+
+ // title_id is now populated for new source of foo
+ assertSoftware(t, expectedSoftware, nil)
+}
diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go
index 8e8153af14..35c8528bb2 100644
--- a/server/fleet/datastore.go
+++ b/server/fleet/datastore.go
@@ -461,6 +461,13 @@ type Datastore interface {
// After aggregation, it cleans up unused software (e.g. software installed
// on removed hosts, software uninstalled on hosts, etc.)
SyncHostsSoftware(ctx context.Context, updatedAt time.Time) error
+
+ // ReconcileSoftwareTitles ensures the software_titles and software tables are in sync.
+ // It inserts new software titles and updates the software table with the title_id.
+ // It also cleans up any software titles that are no longer associated with any software.
+ // It is intended to be run after SyncHostsSoftware.
+ ReconcileSoftwareTitles(ctx context.Context) error
+
// HostVulnSummariesBySoftwareIDs returns a list of all hosts that have at least one of the
// specified Software installed. Includes the path were the software was installed.
HostVulnSummariesBySoftwareIDs(ctx context.Context, softwareIDs []uint) ([]HostVulnerabilitySummary, error)
diff --git a/server/fleet/software.go b/server/fleet/software.go
index 69bec5f575..c7e345d886 100644
--- a/server/fleet/software.go
+++ b/server/fleet/software.go
@@ -102,6 +102,14 @@ func (s Software) ToUniqueStr() string {
return strings.Join(ss, SoftwareFieldSeparator)
}
+type SoftwareTitle struct {
+ ID uint `json:"id" db:"id"`
+ // Name is the name reported by osquery.
+ Name string `json:"name" db:"name"`
+ // Source is the source reported by osquery.
+ Source string `json:"source" db:"source"`
+}
+
// AuthzSoftwareInventory is used for access controls on software inventory.
type AuthzSoftwareInventory struct {
// TeamID is the ID of the team. A value of nil means global scope.
diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go
index 33a87fb82e..01d0ce92fe 100644
--- a/server/mock/datastore_mock.go
+++ b/server/mock/datastore_mock.go
@@ -346,6 +346,8 @@ type ListSoftwareByHostIDShortFunc func(ctx context.Context, hostID uint) ([]fle
type SyncHostsSoftwareFunc func(ctx context.Context, updatedAt time.Time) error
+type ReconcileSoftwareTitlesFunc func(ctx context.Context) error
+
type HostVulnSummariesBySoftwareIDsFunc func(ctx context.Context, softwareIDs []uint) ([]fleet.HostVulnerabilitySummary, error)
type HostsByCVEFunc func(ctx context.Context, cve string) ([]fleet.HostVulnerabilitySummary, error)
@@ -1255,6 +1257,9 @@ type DataStore struct {
SyncHostsSoftwareFunc SyncHostsSoftwareFunc
SyncHostsSoftwareFuncInvoked bool
+ ReconcileSoftwareTitlesFunc ReconcileSoftwareTitlesFunc
+ ReconcileSoftwareTitlesFuncInvoked bool
+
HostVulnSummariesBySoftwareIDsFunc HostVulnSummariesBySoftwareIDsFunc
HostVulnSummariesBySoftwareIDsFuncInvoked bool
@@ -3030,6 +3035,13 @@ func (s *DataStore) SyncHostsSoftware(ctx context.Context, updatedAt time.Time)
return s.SyncHostsSoftwareFunc(ctx, updatedAt)
}
+func (s *DataStore) ReconcileSoftwareTitles(ctx context.Context) error {
+ s.mu.Lock()
+ s.ReconcileSoftwareTitlesFuncInvoked = true
+ s.mu.Unlock()
+ return s.ReconcileSoftwareTitlesFunc(ctx)
+}
+
func (s *DataStore) HostVulnSummariesBySoftwareIDs(ctx context.Context, softwareIDs []uint) ([]fleet.HostVulnerabilitySummary, error) {
s.mu.Lock()
s.HostVulnSummariesBySoftwareIDsFuncInvoked = true
From 4bf263545365f4ef6a320ca400321ab718f9848d Mon Sep 17 00:00:00 2001
From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:16:55 -0500
Subject: [PATCH 07/12] Enroll hosts docs (#15374)
- Move "Grant full disk access for macOS hosts" to Advanced section
---
docs/Using Fleet/enroll-hosts.md | 30 ++++++++++++++----------------
1 file changed, 14 insertions(+), 16 deletions(-)
diff --git a/docs/Using Fleet/enroll-hosts.md b/docs/Using Fleet/enroll-hosts.md
index 571fe6ac92..bfc2b0e7cd 100644
--- a/docs/Using Fleet/enroll-hosts.md
+++ b/docs/Using Fleet/enroll-hosts.md
@@ -48,10 +48,6 @@ With hosts segmented into teams, you can apply unique queries and give users acc
To generate an installer that enrolls to a specific team: from the **Hosts** page, select the desired team from the menu at the top of the screen, then follow the instructions above for generating an installer. The team's enroll secret will be included in the generated command.
-
-
-
-
### Enroll multiple hosts
If you're managing an enterprise environment with multiple hosts, you likely have an enterprise deployment tool like [Munki](https://www.munki.org/munki/), [Jamf Pro](https://www.jamf.com/products/jamf-pro/), [Chef](https://www.chef.io/), [Ansible](https://www.ansible.com/), or [Puppet](https://puppet.com/) to deliver software to your hosts.
@@ -108,7 +104,15 @@ In the Google Admin console:
5. Enter the **Extension ID** and **Installation URL** using the data provided in the modal.
6. Under **Installation Policy**, select **Block**.
-## Grant full disk access to osquery on macOS
+## Advanced
+
+- [Grant full disk access to osquery on macOS](#grant-full-disk-access-to-osquery-on-macos)
+- [Signing fleetd installer](#signing-fleetd-installer)
+- [Generating Windows installers using local WiX toolset](#generating-windows-installers-using-local-wix-toolset)
+- [fleetd configuration options](#fleetd-configuration-options)
+- [Enroll hosts with plain osquery](#enroll-hosts-with-plain-osquery)
+
+### Grant full disk access to osquery on macOS
macOS does not allow applications to access all system files by default. If you are using MDM, which
is required to deploy these profiles, you
@@ -117,8 +121,9 @@ access. This is necessary to query for files located in protected paths as well
tables that require access to the [EndpointSecurity
API](https://developer.apple.com/documentation/endpointsecurity#overview), such as *es_process_events*.
-### Creating the configuration profile
-#### Obtaining identifiers
+#### Creating the configuration profile
+
+##### Obtaining identifiers
If you use plain osquery, instructions are [available here](https://osquery.readthedocs.io/en/stable/deployment/process-auditing/).
On a system with osquery installed via the Fleet osquery installer (fleetd), obtain the
@@ -142,7 +147,7 @@ Note down the **executable path** and the entire **identifier**.
Osqueryd will inherit the privileges from Orbit and does not need explicit permissions.
-#### Creating the profile
+##### Creating the profile
Depending on your MDM, this might be possible in the UI or require a custom profile. If your MDM has a feature to configure *Policy Preferences*, follow these steps:
1. Configure the identifier type to “path.”
@@ -154,7 +159,7 @@ If your MDM does not have built-in support for privacy preferences profiles, you
[PPPC-Utility](https://github.com/jamf/PPPC-Utility) to create a profile with those values, then upload it to
your MDM as a custom profile.
-#### Test the profile
+##### Test the profile
Link the profile to a test group that contains at least one Mac.
Once the computer has received the profile, which you can verify by looking at *Profiles* in *System
Preferences*, run this query from Fleet:
@@ -176,13 +181,6 @@ See the last hour of logs related to TCC permissions with this command:
You can then look for `orbit` or `osquery` to narrow down results.
-## Advanced
-
-- [Signing fleetd installer](#signing-fleetd-installer)
-- [Generating Windows installers using local WiX toolset](#generating-windows-installers-using-local-wix-toolset)
-- [fleetd configuration options](#fleetd-configuration-options)
-- [Enroll hosts with plain osquery](#enroll-hosts-with-plain-osquery)
-
### Signing fleetd installers
>**Note:** Currently, the `fleetctl package` command does not support signing Windows fleetd installers. Windows installers can be signed after building.
From bd5418451085c32c238d1b973ce372014845839e Mon Sep 17 00:00:00 2001
From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:18:18 -0500
Subject: [PATCH 08/12] Puppet module docs (#15336)
- We learned from a customer that the Puppet module requires global
admin
---
docs/Using Fleet/Puppet-module.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/Using Fleet/Puppet-module.md b/docs/Using Fleet/Puppet-module.md
index 6419aa6f5a..8aa09d03ad 100644
--- a/docs/Using Fleet/Puppet-module.md
+++ b/docs/Using Fleet/Puppet-module.md
@@ -20,7 +20,7 @@ Install [Fleet's Puppet module](https://forge.puppet.com/modules/fleetdm/fleetdm
### Step 2: configure Puppet to talk to Fleet using Heira
-1. Create an API-only user with the GitOps role in Fleet. Instructions for creating an API-only user in Fleet are [here](./fleetctl-CLI.md#create-an-api-only-user).
+1. In Fleet, create an API-only user with the global admin role. Instructions for creating an API-only user are [here](./fleetctl-CLI.md#create-an-api-only-user).
2. Get the API token for your new API-only user. Learn how [here](./fleetctl-CLI.md#get-the-api-token-of-an-api-only-user).
From e673cc6caa72c5da2e21ff62c0ac8a9e9a62c76a Mon Sep 17 00:00:00 2001
From: Noah Talerman <47070608+noahtalerman@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:19:16 -0500
Subject: [PATCH 09/12] Update Enroll hosts doc page (#15279)
- Remove section about plain osquery and launcher. Using fleetd is best
practice and we want all new Fleet users to follow this best practice.
If they can't we want to learn why.
- Replace "Fleetd configuration options" section with a tip. Easier to
maintain as we add/update flags.
---
docs/Using Fleet/enroll-hosts.md | 45 ++------------------------------
1 file changed, 2 insertions(+), 43 deletions(-)
diff --git a/docs/Using Fleet/enroll-hosts.md b/docs/Using Fleet/enroll-hosts.md
index bfc2b0e7cd..cc79ef3441 100644
--- a/docs/Using Fleet/enroll-hosts.md
+++ b/docs/Using Fleet/enroll-hosts.md
@@ -6,14 +6,10 @@ Fleet gathers information from an [osquery](https://github.com/osquery/osquery)
You can enroll macOS, Windows or Linux hosts via the [CLI](#cli) or [UI](#ui). To learn how to enroll Chromebooks, see [Enroll Chromebooks](#enroll-chromebooks).
-
### Supported osquery versions
Fleet supports the [latest version of osquery](https://github.com/osquery/osquery/tags).
-
-
-
## CLI
> You must have `fleetctl` installed. [Learn how to install `fleetctl`](https://fleetdm.com/fleetctl-preview).
@@ -35,6 +31,8 @@ Generate macOS installer (.pkg)
fleetctl package --type pkg --fleet-url=example.fleetinstance.com --enroll-secret=85O6XRG8'!l~P&zWt_'f&$QK(sM8_D4x
```
+Tip: To see all options for `fleetctl package` command, run `fleetctl package -h` in your Terminal.
+
## UI
To generate an installer in Fleet UI:
@@ -216,45 +214,6 @@ so:
```
If the provided path doesn't contain all 3 binaries, the command will fail.
-### Fleetd configuration options
-
-The following command-line flags to `fleetctl package` allow you to configure an osquery installer further to communicate with a specific Fleet instance.
-
-| Flag | Options |
-| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
-| --type | **Required** - Type of package to build.
Options: `pkg`(macOS),`msi`(Windows), `deb`(Debian based Linux), `rpm`(RHEL, CentOS, etc.) |
-| --fleet-desktop | Include Fleet Desktop. |
-| --enroll-secret | Enroll secret for authenticating to Fleet server |
-| --fleet-url | URL (`host:port`) of Fleet server |
-| --fleet-certificate | Path to server certificate bundle |
-| --identifier | Identifier for package product (default: `com.fleetdm.orbit`) |
-| --version | Version for package product (default: `0.0.3`) |
-| --insecure | Disable TLS certificate verification (default: `false`) |
-| --service | Install osquery with a persistence service (launchd, systemd, etc.) (default: `true`) |
-| --sign-identity | Identity to use for macOS codesigning |
-| --notarize | Whether to notarize macOS packages (default: `false`) |
-| --disable-updates | Disable auto updates on the generated package (default: false) |
-| --osqueryd-channel | Update channel of osqueryd to use (default: `stable`) |
-| --orbit-channel | Update channel of Orbit to use (default: `stable`) |
-| --desktop-channel | Update channel of desktop to use (default: `stable`) |
-| --update-url | URL for update server (default: `https://tuf.fleetctl.com`) |
-| --update-roots | Root key JSON metadata for update server (from fleetctl updates roots) |
-| --use-system-configuration | Try to read --fleet-url and --enroll-secret using configuration in the host (currently only macOS profiles are supported) |
-| --enable-scripts | Enable script execution (default: `false`) |
-| --debug | Enable debug logging (default: `false`) |
-| --verbose | Log detailed information when building the package (default: false) |
-| --local-wix-dir | Use local installations of the 3 WiX v3 binaries this command uses (`heat.exe`, `candle.exe`, and `light.exe`) instead of installations in a pre-configered Docker Hub (only available on Windows w/ WiX v3) |
-| --help, -h | show help (default: `false`) |
-
-Fleet supports other methods for adding your hosts to Fleet, such as the plain osquery
-binaries or [Kolide Osquery
-Launcher](https://github.com/kolide/launcher/blob/master/docs/launcher.md#connecting-to-fleet).
-
-### Enroll hosts with plain osquery
-
-Osquery's [TLS API](http://osquery.readthedocs.io/en/stable/deployment/remote/) plugin lets you use
-the native osqueryd binaries to connect to Fleet. Learn more [here](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Enroll-hosts-with-plain-osquery.md).
-
From 29580a242de9fdb3113d4c02b52e0f8f97a3cd75 Mon Sep 17 00:00:00 2001
From: Sharon Katz <121527325+sharon-fdm@users.noreply.github.com>
Date: Mon, 4 Dec 2023 14:26:26 -0500
Subject: [PATCH 10/12] 15135 remove atom package (#15410)
---
...onsider-joining-against-the-users-table.md | 1 -
.../standard-query-library.yml | 4 +--
docs/Using Fleet/Audit-logs.md | 8 ++---
docs/Using Fleet/Understanding-host-vitals.md | 30 -------------------
frontend/interfaces/software.ts | 2 +-
.../cards/Software/SoftwareTableConfig.tsx | 2 +-
schema/fleet_schema.json | 10 -------
schema/tables/atom_packages.yml | 23 +++++++-------
tools/osquery-testing/macOS.txt | 1 -
9 files changed, 20 insertions(+), 61 deletions(-)
diff --git a/articles/osquery-consider-joining-against-the-users-table.md b/articles/osquery-consider-joining-against-the-users-table.md
index 3382188dc9..9b7dc904d6 100644
--- a/articles/osquery-consider-joining-against-the-users-table.md
+++ b/articles/osquery-consider-joining-against-the-users-table.md
@@ -30,7 +30,6 @@ Our query runs as expected when `osqueryi` is run as a normal user, but returns
This same issue manifests on many tables that include a `uid` column:
-- `atom_packages`
- `authorized_keys`
- `chrome_extension_content_scripts`
- `chrome_extensions`
diff --git a/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml b/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml
index bb43ca47de..6e0995c34c 100644
--- a/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml
+++ b/docs/01-Using-Fleet/standard-query-library/standard-query-library.yml
@@ -61,7 +61,7 @@ spec:
name: Get installed Linux software
platform: linux
description: Get all software installed on a Linux computer, including browser plugins and installed packages. Note that this does not include other running processes in the processes table.
- query: SELECT name AS name, version AS version, 'Package (APT)' AS type, 'apt_sources' AS source FROM apt_sources UNION SELECT name AS name, version AS version, 'Package (deb)' AS type, 'deb_packages' AS source FROM deb_packages UNION SELECT package AS name, version AS version, 'Package (Portage)' AS type, 'portage_packages' AS source FROM portage_packages UNION SELECT name AS name, version AS version, 'Package (RPM)' AS type, 'rpm_packages' AS source FROM rpm_packages UNION SELECT name AS name, '' AS version, 'Package (YUM)' AS type, 'yum_sources' AS source FROM yum_sources UNION SELECT name AS name, version AS version, 'Package (NPM)' AS type, 'npm_packages' AS source FROM npm_packages UNION SELECT name AS name, version AS version, 'Package (Atom)' AS type, 'atom_packages' AS source FROM atom_packages UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages;
+ query: SELECT name AS name, version AS version, 'Package (APT)' AS type, 'apt_sources' AS source FROM apt_sources UNION SELECT name AS name, version AS version, 'Package (deb)' AS type, 'deb_packages' AS source FROM deb_packages UNION SELECT package AS name, version AS version, 'Package (Portage)' AS type, 'portage_packages' AS source FROM portage_packages UNION SELECT name AS name, version AS version, 'Package (RPM)' AS type, 'rpm_packages' AS source FROM rpm_packages UNION SELECT name AS name, '' AS version, 'Package (YUM)' AS type, 'yum_sources' AS source FROM yum_sources UNION SELECT name AS name, version AS version, 'Package (NPM)' AS type, 'npm_packages' AS source FROM npm_packages UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages;
purpose: Informational
tags: inventory, built-in
contributors: zwass
@@ -94,7 +94,7 @@ spec:
name: Get installed Windows software
platform: windows
description: Get all software installed on a Windows computer, including programs, browser plugins, and installed packages. Note that this does not include other running processes in the processes table.
- query: SELECT name AS name, version AS version, 'Program (Windows)' AS type, 'programs' AS source FROM programs UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages UNION SELECT name AS name, version AS version, 'Browser plugin (IE)' AS type, 'ie_extensions' AS source FROM ie_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Chrome)' AS type, 'chrome_extensions' AS source FROM chrome_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Firefox)' AS type, 'firefox_addons' AS source FROM firefox_addons UNION SELECT name AS name, version AS version, 'Package (Chocolatey)' AS type, 'chocolatey_packages' AS source FROM chocolatey_packages UNION SELECT name AS name, version AS version, 'Package (Atom)' AS type, 'atom_packages' AS source FROM atom_packages;
+ query: SELECT name AS name, version AS version, 'Program (Windows)' AS type, 'programs' AS source FROM programs UNION SELECT name AS name, version AS version, 'Package (Python)' AS type, 'python_packages' AS source FROM python_packages UNION SELECT name AS name, version AS version, 'Browser plugin (IE)' AS type, 'ie_extensions' AS source FROM ie_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Chrome)' AS type, 'chrome_extensions' AS source FROM chrome_extensions UNION SELECT name AS name, version AS version, 'Browser plugin (Firefox)' AS type, 'firefox_addons' AS source FROM firefox_addons UNION SELECT name AS name, version AS version, 'Package (Chocolatey)' AS type, 'chocolatey_packages' AS source FROM chocolatey_packages;
purpose: Informational
tags: inventory, built-in
contributors: zwass
diff --git a/docs/Using Fleet/Audit-logs.md b/docs/Using Fleet/Audit-logs.md
index a27e15997c..f530dedc7b 100644
--- a/docs/Using Fleet/Audit-logs.md
+++ b/docs/Using Fleet/Audit-logs.md
@@ -610,7 +610,7 @@ This activity contains the following fields:
}
```
-## `edited_windows_updates`
+## edited_windows_updates
Generated when the Windows OS updates deadline or grace period is modified.
@@ -939,7 +939,7 @@ This activity contains the following fields:
}
```
-### Type `created_windows_profile`
+## created_windows_profile
Generated when a user adds a new Windows profile to a team (or no team).
@@ -958,7 +958,7 @@ This activity contains the following fields:
}
```
-### Type `deleted_windows_profile`
+## deleted_windows_profile
Generated when a user deletes a Windows profile from a team (or no team).
@@ -977,7 +977,7 @@ This activity contains the following fields:
}
```
-### Type `edited_windows_profile`
+## edited_windows_profile
Generated when a user edits the Windows profiles of a team (or no team) via the fleetctl CLI.
diff --git a/docs/Using Fleet/Understanding-host-vitals.md b/docs/Using Fleet/Understanding-host-vitals.md
index 1e82f652ec..f51fad9ffd 100644
--- a/docs/Using Fleet/Understanding-host-vitals.md
+++ b/docs/Using Fleet/Understanding-host-vitals.md
@@ -544,17 +544,6 @@ SELECT
path AS installed_path
FROM cached_users CROSS JOIN firefox_addons USING (uid)
UNION
-SELECT
- name AS name,
- version AS version,
- 'Package (Atom)' AS type,
- 'atom_packages' AS source,
- '' AS release,
- '' AS vendor,
- '' AS arch,
- path AS installed_path
-FROM cached_users CROSS JOIN atom_packages USING (uid)
-UNION
SELECT
name AS name,
version AS version,
@@ -628,16 +617,6 @@ SELECT
path AS installed_path
FROM cached_users CROSS JOIN safari_extensions USING (uid)
UNION
-SELECT
- name AS name,
- version AS version,
- 'Package (Atom)' AS type,
- '' AS bundle_identifier,
- 'atom_packages' AS source,
- 0 AS last_opened_at,
- path AS installed_path
-FROM cached_users CROSS JOIN atom_packages USING (uid)
-UNION
SELECT
name AS name,
version AS version,
@@ -713,15 +692,6 @@ SELECT
'' AS vendor,
path AS installed_path
FROM chocolatey_packages
-UNION
-SELECT
- name AS name,
- version AS version,
- 'Package (Atom)' AS type,
- 'atom_packages' AS source,
- '' AS vendor,
- path AS installed_path
-FROM cached_users CROSS JOIN atom_packages USING (uid);
```
## system_info
diff --git a/frontend/interfaces/software.ts b/frontend/interfaces/software.ts
index 9230dfc1b8..7b285004a9 100644
--- a/frontend/interfaces/software.ts
+++ b/frontend/interfaces/software.ts
@@ -43,7 +43,7 @@ export const TYPE_CONVERSION: Record = {
rpm_packages: "Package (RPM)",
yum_sources: "Package (YUM)",
npm_packages: "Package (NPM)",
- atom_packages: "Package (Atom)",
+ atom_packages: "Package (Atom)", // Atom packages were removed from software inventory. Mapping is maintained for backwards compatibility. (2023-12-04)
python_packages: "Package (Python)",
apps: "Application (macOS)",
chrome_extensions: "Browser plugin (Chrome)",
diff --git a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
index 8e7e244835..b95f38b3f8 100644
--- a/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SoftwareTableConfig.tsx
@@ -75,7 +75,7 @@ const TYPE_CONVERSION: Record = {
rpm_packages: "Package (RPM)",
yum_sources: "Package (YUM)",
npm_packages: "Package (NPM)",
- atom_packages: "Package (Atom)",
+ atom_packages: "Package (Atom)", // Atom packages were removed from software inventory. Mapping is maintained for backwards compatibility. (2023-12-04)
python_packages: "Package (Python)",
apps: "Application (macOS)",
chrome_extensions: "Browser plugin (Chrome)",
diff --git a/schema/fleet_schema.json b/schema/fleet_schema.json
index d4490ebe97..3930a691c5 100644
--- a/schema/fleet_schema.json
+++ b/schema/fleet_schema.json
@@ -992,16 +992,6 @@
}
]
},
- {
- "name": "atom_packages",
- "examples": "List installed Atom packages and their version.\n```\nSELECT name, version, description FROM atom_packages;\n```",
- "columns": [
- {
- "name": "uid",
- "requires_user_context": true
- }
- ]
- },
{
"name": "chrome_extension_content_scripts",
"columns": [
diff --git a/schema/tables/atom_packages.yml b/schema/tables/atom_packages.yml
index 4d795cca92..4db7c9286b 100644
--- a/schema/tables/atom_packages.yml
+++ b/schema/tables/atom_packages.yml
@@ -1,12 +1,13 @@
name: atom_packages
-examples: >-
- List installed Atom packages and their version.
-
- ```
-
- SELECT name, version, description FROM atom_packages;
-
- ```
-columns:
- - name: uid
- requires_user_context: true
+hidden: true
+# examples: >-
+# List installed Atom packages and their version.
+#
+# ```
+#
+# SELECT name, version, description FROM atom_packages;
+#
+# ```
+# columns:
+# - name: uid
+# requires_user_context: true
diff --git a/tools/osquery-testing/macOS.txt b/tools/osquery-testing/macOS.txt
index 4969c39db1..00e39cae90 100644
--- a/tools/osquery-testing/macOS.txt
+++ b/tools/osquery-testing/macOS.txt
@@ -10,7 +10,6 @@ app_schemes SELECT * FROM app_schemes
apps SELECT * FROM apps
arp_cache SELECT * FROM arp_cache
asl SELECT * FROM asl
-atom_packages SELECT * FROM atom_packages
augeas SELECT * from augeas where path = '/etc/hosts'
authorization_mechanisms SELECT * FROM authorization_mechanisms
authorizations SELECT * FROM authorizations
From 9b40573f5432fd64834441b0e5320b259ef68db0 Mon Sep 17 00:00:00 2001
From: Lucas Manuel Rodriguez
Date: Mon, 4 Dec 2023 17:14:16 -0300
Subject: [PATCH 11/12] Fix `installed_from_dep` value in activity for DEP
re-enrolling (#15419)
#12037

- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
---
changes/12037-fix-dep-reenroll-activity | 1 +
server/datastore/mysql/apple_mdm.go | 2 +-
server/service/apple_mdm.go | 2 +-
server/service/integration_mdm_test.go | 25 +++++++++++++++++++++++++
4 files changed, 28 insertions(+), 2 deletions(-)
create mode 100644 changes/12037-fix-dep-reenroll-activity
diff --git a/changes/12037-fix-dep-reenroll-activity b/changes/12037-fix-dep-reenroll-activity
new file mode 100644
index 0000000000..277e78d6b2
--- /dev/null
+++ b/changes/12037-fix-dep-reenroll-activity
@@ -0,0 +1 @@
+* Fix `installed_from_dep` in `mdm_enrolled` activity when a DEP device unenrolls and re-enrolls.
diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go
index 50c93879b0..834c87fdb9 100644
--- a/server/datastore/mysql/apple_mdm.go
+++ b/server/datastore/mysql/apple_mdm.go
@@ -984,7 +984,7 @@ func (ds *Datastore) UpdateHostTablesOnMDMUnenroll(ctx context.Context, uuid str
return ctxerr.Wrap(ctx, err, "getting host id from UUID")
}
- // NOTE: set installed_from_dep = 0 so DEP host will not be counted as pending after it unrolls
+ // NOTE: set installed_from_dep = 0 so DEP host will not be counted as pending after it unenrolls.
_, err = tx.ExecContext(ctx, `
UPDATE host_mdm SET enrolled = 0, installed_from_dep = 0, server_url = '', mdm_id = NULL WHERE host_id = ?`, hostID)
if err != nil {
diff --git a/server/service/apple_mdm.go b/server/service/apple_mdm.go
index 5abeb2af0e..253428b9fc 100644
--- a/server/service/apple_mdm.go
+++ b/server/service/apple_mdm.go
@@ -2206,7 +2206,7 @@ func (svc *MDMAppleCheckinAndCommandService) Authenticate(r *mdm.Request, m *mdm
return svc.ds.NewActivity(r.Context, nil, &fleet.ActivityTypeMDMEnrolled{
HostSerial: info.HardwareSerial,
HostDisplayName: info.DisplayName,
- InstalledFromDEP: info.InstalledFromDEP,
+ InstalledFromDEP: info.DEPAssignedToFleet,
MDMPlatform: fleet.MDMPlatformApple,
})
}
diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go
index 49b46d85b6..e29bb5c655 100644
--- a/server/service/integration_mdm_test.go
+++ b/server/service/integration_mdm_test.go
@@ -2196,6 +2196,31 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
require.NoError(t, mdmDevice.Enroll())
checkPostEnrollmentCommands(mdmDevice, true)
+ // The user unenrolls from Fleet (e.g. was DEP enrolled but with `is_mdm_removable: true`
+ // so the user removes the enrollment profile).
+ err = mdmDevice.Checkout()
+ require.NoError(t, err)
+
+ // Simulate a refetch where we clean up the MDM data since the host is not enrolled anymore
+ mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
+ _, err := q.ExecContext(ctx, `DELETE FROM host_mdm WHERE host_id = ?`, mdmDeviceID)
+ return err
+ })
+
+ // Simulate fleetd re-enrolling automatically.
+ err = mdmDevice.Enroll()
+ require.NoError(t, err)
+
+ // The last activity should have `installed_from_dep=true`.
+ s.lastActivityMatches(
+ "mdm_enrolled",
+ fmt.Sprintf(
+ `{"host_serial": "%s", "host_display_name": "%s (%s)", "installed_from_dep": true, "mdm_platform": "apple"}`,
+ mdmDevice.SerialNumber, mdmDevice.Model, mdmDevice.SerialNumber,
+ ),
+ 0,
+ )
+
// enroll a host into Fleet
eHost, err := s.ds.NewHost(context.Background(), &fleet.Host{
ID: 1,
From 3fe08fecba03d0ae3335f30e894daedbc4107cd3 Mon Sep 17 00:00:00 2001
From: Eric
Date: Mon, 4 Dec 2023 14:59:26 -0600
Subject: [PATCH 12/12] Website: Handle scrolling to in-page links when query
parameters are provided. (#15423)
Closes: #15415
Changes:
- Updated the documentation and handbook page scripts to navigate users
who visit a URL with a hash link with query parameters attached to the
correct section.
---
.../js/pages/docs/basic-documentation.page.js | 15 +++++++++++----
.../js/pages/handbook/basic-handbook.page.js | 15 +++++++++++----
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/website/assets/js/pages/docs/basic-documentation.page.js b/website/assets/js/pages/docs/basic-documentation.page.js
index 7e4371e9c7..8a66b41c73 100644
--- a/website/assets/js/pages/docs/basic-documentation.page.js
+++ b/website/assets/js/pages/docs/basic-documentation.page.js
@@ -108,11 +108,18 @@ parasails.registerPage('basic-documentation', {
// Handle hashes in urls when coming from an external page.
if(window.location.hash){
- let possibleHashToScrollTo = _.trimLeft(window.location.hash, '#');
- let hashToScrollTo = document.getElementById(possibleHashToScrollTo);
+ // If a hash was provided, we'll remove the # and any query parameters from it. (e.g., #create-an-api-only-user?utm_medium=fleetui&utm_campaign=get-api-token » create-an-api-only-user)
+ // Note: Hash links for headings in markdown content will never have a '?' beacause they are removed when convereted to kebab-case, so we can safely strip everything after one if a url contains a query parameter.
+ let possibleHashToScrollTo = _.trimLeft(window.location.hash.split('?')[0], '#');
+ let elementWithMatchingId = document.getElementById(possibleHashToScrollTo);
// If the hash matches a header's ID, we'll scroll to that section.
- if(hashToScrollTo){
- hashToScrollTo.scrollIntoView();
+ if(elementWithMatchingId){
+ // Get the distance of the specified element, and reduce it by 90 so the section is not hidden by the page header.
+ let amountToScroll = elementWithMatchingId.offsetTop - 90;
+ window.scrollTo({
+ top: amountToScroll,
+ left: 0,
+ });
}
}
diff --git a/website/assets/js/pages/handbook/basic-handbook.page.js b/website/assets/js/pages/handbook/basic-handbook.page.js
index 7279dec309..4053bf5fd6 100644
--- a/website/assets/js/pages/handbook/basic-handbook.page.js
+++ b/website/assets/js/pages/handbook/basic-handbook.page.js
@@ -52,11 +52,18 @@ parasails.registerPage('basic-handbook', {
// Handle hashes in urls when coming from an external page.
if(window.location.hash){
- let possibleHashToScrollTo = _.trimLeft(window.location.hash, '#');
- let hashToScrollTo = document.getElementById(possibleHashToScrollTo);
+ // If a hash was provided, we'll remove the # and any query parameters from it. (e.g., #create-an-api-only-user?utm_medium=fleetui&utm_campaign=get-api-token » create-an-api-only-user)
+ // Note: Hash links for headings in markdown content will never have a '?' beacause they are removed when convereted to kebab-case, so we can safely strip everything after one if a url contains a query parameter.
+ let possibleHashToScrollTo = _.trimLeft(window.location.hash.split('?')[0], '#');
+ let elementWithMatchingId = document.getElementById(possibleHashToScrollTo);
// If the hash matches a header's ID, we'll scroll to that section.
- if(hashToScrollTo){
- hashToScrollTo.scrollIntoView();
+ if(elementWithMatchingId){
+ // Get the distance of the specified element, and reduce it by 90 so the section is not hidden by the page header.
+ let amountToScroll = elementWithMatchingId.offsetTop - 90;
+ window.scrollTo({
+ top: amountToScroll,
+ left: 0,
+ });
}
}