chore: merge main
This commit is contained in:
@@ -323,15 +323,16 @@ func processFailingHostExistingCalendarEvent(
|
||||
// Try to acquire the lock. Lock is needed to ensure calendar callback is not processed for this event at the same time.
|
||||
eventUUID := calendarEvent.UUID
|
||||
lockValue := uuid.New().String()
|
||||
lockAcquired, err := distributedLock.AcquireLock(ctx, calendar.LockKeyPrefix+eventUUID, lockValue, 0)
|
||||
lockAcquired, err := distributedLock.AcquireLock(ctx, calendar.LockKeyPrefix+eventUUID, lockValue, calendar.DistributedLockExpireMs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("acquire calendar lock: %w", err)
|
||||
}
|
||||
|
||||
lockReserved := false
|
||||
if !lockAcquired {
|
||||
// Lock was not acquired. We reserve the lock and try to acquire it until we do.
|
||||
var timeoutMs uint64 = 2 * 60 * 1000
|
||||
lockAcquired, err = distributedLock.AcquireLock(ctx, calendar.ReservedLockKeyPrefix+eventUUID, lockValue, timeoutMs)
|
||||
lockAcquired, err = distributedLock.AcquireLock(ctx, calendar.ReservedLockKeyPrefix+eventUUID, lockValue,
|
||||
calendar.ReserveLockExpireMs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reserve calendar lock: %w", err)
|
||||
}
|
||||
@@ -344,12 +345,13 @@ func processFailingHostExistingCalendarEvent(
|
||||
go func() {
|
||||
for {
|
||||
// Keep trying to get the lock.
|
||||
lockAcquired, err = distributedLock.AcquireLock(ctx, calendar.LockKeyPrefix+eventUUID, lockValue, 0)
|
||||
lockAcquired, err = distributedLock.AcquireLock(ctx, calendar.LockKeyPrefix+eventUUID, lockValue,
|
||||
calendar.DistributedLockExpireMs)
|
||||
if err != nil || lockAcquired {
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
@@ -358,7 +360,7 @@ func processFailingHostExistingCalendarEvent(
|
||||
if err != nil {
|
||||
return fmt.Errorf("try to acquire calendar lock: %w", err)
|
||||
}
|
||||
case <-time.After(time.Duration(timeoutMs) * time.Millisecond):
|
||||
case <-time.After(time.Duration(calendar.ReserveLockExpireMs) * time.Millisecond):
|
||||
// We couldn't acquire the lock in time.
|
||||
return errors.New("could not acquire calendar lock in time")
|
||||
}
|
||||
@@ -372,7 +374,7 @@ func processFailingHostExistingCalendarEvent(
|
||||
}
|
||||
if !ok {
|
||||
// If the lock was not released, it will expire on its own.
|
||||
level.Warn(logger).Log("msg", "Failed to release calendar reserve lock")
|
||||
level.Error(logger).Log("msg", "Failed to release calendar reserve lock", "event uuid", eventUUID, "lockValue", lockValue)
|
||||
}
|
||||
}
|
||||
ok, err := distributedLock.ReleaseLock(ctx, calendar.LockKeyPrefix+eventUUID, lockValue)
|
||||
@@ -380,8 +382,8 @@ func processFailingHostExistingCalendarEvent(
|
||||
level.Error(logger).Log("msg", "Failed to release calendar lock", "err", err)
|
||||
}
|
||||
if !ok {
|
||||
// If the lock was not released, it will expire on its own.
|
||||
level.Warn(logger).Log("msg", "Failed to release calendar lock")
|
||||
// If the lock was not released, it will expire on its own. However, we should adjust expiration time or something else to make sure we don't get here.
|
||||
level.Error(logger).Log("msg", "Failed to release calendar lock", "event uuid", eventUUID, "lockValue", lockValue)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -390,11 +392,23 @@ func processFailingHostExistingCalendarEvent(
|
||||
now := time.Now()
|
||||
|
||||
if calendarConfig.AlwaysReloadEvent() || shouldReloadCalendarEvent(now, calendarEvent, hostCalendarEvent) {
|
||||
var err error
|
||||
// Refetch the event since it may have updated since we got the lock.
|
||||
// We need the latest event data (ETag) to make sure that we get correct data from the calendar service.
|
||||
calendarEvent, err = ds.GetCalendarEvent(ctx, calendarEvent.Email)
|
||||
if err != nil {
|
||||
if fleet.IsNotFound(err) {
|
||||
// Event was deleted while we were processing it. It will be recreated if needed on the next cron run
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("get calendar event from db: %w", err)
|
||||
}
|
||||
// We could check the updated_at timestamp and avoid updating the event if it was updated recently.
|
||||
|
||||
updatedEvent, _, err = userCalendar.GetAndUpdateEvent(
|
||||
calendarEvent, func(conflict bool) (string, bool, error) {
|
||||
return calendar.GenerateCalendarEventBody(ctx, ds, orgName, host, policyIDtoPolicy, conflict, logger), true, nil
|
||||
},
|
||||
fleet.CalendarGetAndUpdateEventOpts{UpdateTimezone: true},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get event calendar on db: %w", err)
|
||||
@@ -417,6 +431,7 @@ func processFailingHostExistingCalendarEvent(
|
||||
}
|
||||
|
||||
// Remove event from the queue so that we don't process this event again.
|
||||
// If we just modified the event in the calendar, calendar will send a callback, and we don't need to process that callback.
|
||||
err = distributedLock.RemoveFromSet(ctx, calendar.QueueKey, eventUUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove calendar event from queue: %w", err)
|
||||
@@ -528,7 +543,7 @@ func attemptCreatingEventOnUserCalendar(
|
||||
calendarEvent, err := userCalendar.CreateEvent(
|
||||
preferredDate, func(conflict bool) (string, bool, error) {
|
||||
return calendar.GenerateCalendarEventBody(ctx, ds, orgName, host, policyIDtoPolicy, conflict, logger), true, nil
|
||||
},
|
||||
}, fleet.CalendarCreateEventOpts{},
|
||||
)
|
||||
var dee fleet.DayEndedError
|
||||
switch {
|
||||
@@ -817,6 +832,10 @@ func deleteCalendarEvent(
|
||||
return fmt.Errorf("delete calendar event: %w", err)
|
||||
}
|
||||
}
|
||||
// Stop watching for calendar changes
|
||||
if err := userCalendar.StopEventChannel(calendarEvent); err != nil {
|
||||
return fmt.Errorf("stop event channel: %w", err)
|
||||
}
|
||||
}
|
||||
if err := ds.DeleteCalendarEvent(ctx, calendarEvent.ID); err != nil {
|
||||
return fmt.Errorf("delete db calendar event: %w", err)
|
||||
|
||||
@@ -559,7 +559,7 @@ func TestCachedTeamMDMConfig(t *testing.T) {
|
||||
|
||||
testMDMConfig := fleet.TeamMDM{
|
||||
EnableDiskEncryption: true,
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.10.10"),
|
||||
Deadline: optjson.SetString("1992-03-01"),
|
||||
},
|
||||
@@ -618,7 +618,7 @@ func TestCachedTeamMDMConfig(t *testing.T) {
|
||||
|
||||
// saving a team updates config in cache
|
||||
updateMDMConfig := fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("13.13.13"),
|
||||
Deadline: optjson.SetString("2022-03-01"),
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ CREATE TABLE `app_config_json` (
|
||||
UNIQUE KEY `id` (`id`)
|
||||
) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
INSERT INTO `app_config_json` VALUES (1,'{\"mdm\": {\"ios_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_setup\": {\"bootstrap_package\": null, \"macos_setup_assistant\": null, \"enable_end_user_authentication\": false, \"enable_release_device_manually\": false}, \"macos_updates\": {\"deadline\": null, \"minimum_version\": null}, \"ipados_updates\": {\"deadline\": null, \"minimum_version\": null}, \"macos_settings\": {\"custom_settings\": null}, \"macos_migration\": {\"mode\": \"\", \"enable\": false, \"webhook_url\": \"\"}, \"windows_updates\": {\"deadline_days\": null, \"grace_period_days\": null}, \"windows_settings\": {\"custom_settings\": null}, \"apple_bm_default_team\": \"\", \"apple_bm_terms_expired\": false, \"enable_disk_encryption\": false, \"enabled_and_configured\": false, \"end_user_authentication\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"issuer_uri\": \"\", \"metadata_url\": \"\"}, \"windows_enabled_and_configured\": false, \"apple_bm_enabled_and_configured\": false}, \"scripts\": null, \"features\": {\"enable_host_users\": true, \"enable_software_inventory\": false}, \"org_info\": {\"org_name\": \"\", \"contact_url\": \"\", \"org_logo_url\": \"\", \"org_logo_url_light_background\": \"\"}, \"integrations\": {\"jira\": null, \"zendesk\": null, \"google_calendar\": null}, \"sso_settings\": {\"idp_name\": \"\", \"metadata\": \"\", \"entity_id\": \"\", \"enable_sso\": false, \"issuer_uri\": \"\", \"metadata_url\": \"\", \"idp_image_url\": \"\", \"enable_jit_role_sync\": false, \"enable_sso_idp_login\": false, \"enable_jit_provisioning\": false}, \"agent_options\": {\"config\": {\"options\": {\"logger_plugin\": \"tls\", \"pack_delimiter\": \"/\", \"logger_tls_period\": 10, \"distributed_plugin\": \"tls\", \"disable_distributed\": false, \"logger_tls_endpoint\": \"/api/osquery/log\", \"distributed_interval\": 10, \"distributed_tls_max_attempts\": 3}, \"decorators\": {\"load\": [\"SELECT uuid AS host_uuid FROM system_info;\", \"SELECT hostname AS hostname FROM system_info;\"]}}, \"overrides\": {}}, \"fleet_desktop\": {\"transparency_url\": \"\"}, \"smtp_settings\": {\"port\": 587, \"domain\": \"\", \"server\": \"\", \"password\": \"\", \"user_name\": \"\", \"configured\": false, \"enable_smtp\": false, \"enable_ssl_tls\": true, \"sender_address\": \"\", \"enable_start_tls\": true, \"verify_ssl_certs\": true, \"authentication_type\": \"0\", \"authentication_method\": \"0\"}, \"server_settings\": {\"server_url\": \"\", \"enable_analytics\": false, \"query_report_cap\": 0, \"scripts_disabled\": false, \"deferred_save_host\": false, \"live_query_disabled\": false, \"ai_features_disabled\": false, \"query_reports_disabled\": false}, \"webhook_settings\": {\"interval\": \"0s\", \"activities_webhook\": {\"destination_url\": \"\", \"enable_activities_webhook\": false}, \"host_status_webhook\": {\"days_count\": 0, \"destination_url\": \"\", \"host_percentage\": 0, \"enable_host_status_webhook\": false}, \"vulnerabilities_webhook\": {\"destination_url\": \"\", \"host_batch_size\": 0, \"enable_vulnerabilities_webhook\": false}, \"failing_policies_webhook\": {\"policy_ids\": null, \"destination_url\": \"\", \"host_batch_size\": 0, \"enable_failing_policies_webhook\": false}}, \"host_expiry_settings\": {\"host_expiry_window\": 0, \"host_expiry_enabled\": false}, \"vulnerability_settings\": {\"databases_path\": \"\"}, \"activity_expiry_settings\": {\"activity_expiry_window\": 0, \"activity_expiry_enabled\": false}}','2020-01-01 01:01:01','2020-01-01 01:01:01');
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!50503 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `calendar_events` (
|
||||
|
||||
@@ -591,10 +591,18 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) {
|
||||
Name: "team1",
|
||||
Config: fleet.TeamConfig{
|
||||
MDM: fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2025-10-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("11.11.11"),
|
||||
Deadline: optjson.SetString("2024-04-04"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.12.12"),
|
||||
Deadline: optjson.SetString("2023-03-03"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.SetInt(7),
|
||||
GracePeriodDays: optjson.SetInt(3),
|
||||
@@ -614,10 +622,18 @@ func testTeamsMDMConfig(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, &fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2025-10-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("11.11.11"),
|
||||
Deadline: optjson.SetString("2024-04-04"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.12.12"),
|
||||
Deadline: optjson.SetString("2023-03-03"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.SetInt(7),
|
||||
GracePeriodDays: optjson.SetInt(3),
|
||||
@@ -695,7 +711,6 @@ func testTeamsNameEmoji(t *testing.T, ds *Datastore) {
|
||||
assert.NoError(t, err)
|
||||
require.Len(t, results, 1)
|
||||
assert.Equal(t, emoji1, results[0].Name)
|
||||
|
||||
}
|
||||
|
||||
// Ensure case-insensitive sort order for ames
|
||||
@@ -717,5 +732,4 @@ func testTeamsNameSort(t *testing.T, ds *Datastore) {
|
||||
for i, item := range teams {
|
||||
assert.Equal(t, item.Name, results[i].Name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ var ActivityDetailsList = []ActivityDetails{
|
||||
ActivityTypeMDMUnenrolled{},
|
||||
|
||||
ActivityTypeEditedMacOSMinVersion{},
|
||||
ActivityTypeEditedIOSMinVersion{},
|
||||
ActivityTypeEditedIPadOSMinVersion{},
|
||||
ActivityTypeEditedWindowsUpdates{},
|
||||
|
||||
ActivityTypeReadHostDiskEncryptionKey{},
|
||||
@@ -837,6 +839,56 @@ func (a ActivityTypeEditedWindowsUpdates) Documentation() (activity string, deta
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityTypeEditedIOSMinVersion struct {
|
||||
TeamID *uint `json:"team_id"`
|
||||
TeamName *string `json:"team_name"`
|
||||
MinimumVersion string `json:"minimum_version"`
|
||||
Deadline string `json:"deadline"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeEditedIOSMinVersion) ActivityName() string {
|
||||
return "edited_ios_min_version"
|
||||
}
|
||||
|
||||
func (a ActivityTypeEditedIOSMinVersion) Documentation() (activity string, details string, detailsExample string) {
|
||||
return `Generated when the minimum required iOS version or deadline is modified.`,
|
||||
`This activity contains the following fields:
|
||||
- "team_id": The ID of the team that the minimum iOS version applies to, ` + "`null`" + ` if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that the minimum iOS version applies to, ` + "`null`" + ` if it applies to devices that are not in a team.
|
||||
- "minimum_version": The minimum iOS version required, empty if the requirement was removed.
|
||||
- "deadline": The deadline by which the minimum version requirement must be applied, empty if the requirement was removed.`, `{
|
||||
"team_id": 3,
|
||||
"team_name": "iPhones",
|
||||
"minimum_version": "17.5.1",
|
||||
"deadline": "2023-06-01"
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityTypeEditedIPadOSMinVersion struct {
|
||||
TeamID *uint `json:"team_id"`
|
||||
TeamName *string `json:"team_name"`
|
||||
MinimumVersion string `json:"minimum_version"`
|
||||
Deadline string `json:"deadline"`
|
||||
}
|
||||
|
||||
func (a ActivityTypeEditedIPadOSMinVersion) ActivityName() string {
|
||||
return "edited_ipados_min_version"
|
||||
}
|
||||
|
||||
func (a ActivityTypeEditedIPadOSMinVersion) Documentation() (activity string, details string, detailsExample string) {
|
||||
return `Generated when the minimum required iPadOS version or deadline is modified.`,
|
||||
`This activity contains the following fields:
|
||||
- "team_id": The ID of the team that the minimum iPadOS version applies to, ` + "`null`" + ` if it applies to devices that are not in a team.
|
||||
- "team_name": The name of the team that the minimum iPadOS version applies to, ` + "`null`" + ` if it applies to devices that are not in a team.
|
||||
- "minimum_version": The minimum iPadOS version required, empty if the requirement was removed.
|
||||
- "deadline": The deadline by which the minimum version requirement must be applied, empty if the requirement was removed.`, `{
|
||||
"team_id": 3,
|
||||
"team_name": "iPads",
|
||||
"minimum_version": "17.5.1",
|
||||
"deadline": "2023-06-01"
|
||||
}`
|
||||
}
|
||||
|
||||
type ActivityTypeReadHostDiskEncryptionKey struct {
|
||||
HostID uint `json:"host_id"`
|
||||
HostDisplayName string `json:"host_display_name"`
|
||||
|
||||
+12
-5
@@ -148,7 +148,13 @@ type MDM struct {
|
||||
// backend, should be done only after careful analysis.
|
||||
EnabledAndConfigured bool `json:"enabled_and_configured"`
|
||||
|
||||
MacOSUpdates MacOSUpdates `json:"macos_updates"`
|
||||
// MacOSUpdates defines the OS update settings for macOS devices.
|
||||
MacOSUpdates AppleOSUpdateSettings `json:"macos_updates"`
|
||||
// IOSUpdates defines the OS update settings for iOS devices.
|
||||
IOSUpdates AppleOSUpdateSettings `json:"ios_updates"`
|
||||
// IPadOSUpdates defines the OS update settings for iPadOS devices.
|
||||
IPadOSUpdates AppleOSUpdateSettings `json:"ipados_updates"`
|
||||
// WindowsUpdates defines the OS update settings for Windows devices.
|
||||
WindowsUpdates WindowsUpdates `json:"windows_updates"`
|
||||
|
||||
MacOSSettings MacOSSettings `json:"macos_settings"`
|
||||
@@ -182,8 +188,9 @@ func (m MDM) AtLeastOnePlatformEnabledAndConfigured() bool {
|
||||
// format only (no prerelease or build metadata).
|
||||
var versionStringRegex = regexp.MustCompile(`^\d+(\.\d+)?(\.\d+)?$`)
|
||||
|
||||
// MacOSUpdates is part of AppConfig and defines the macOS update settings.
|
||||
type MacOSUpdates struct {
|
||||
// AppleOSUpdateSettings is the common type that contains the settings
|
||||
// for OS updates on Apple devices.
|
||||
type AppleOSUpdateSettings struct {
|
||||
// MinimumVersion is the required minimum operating system version.
|
||||
MinimumVersion optjson.String `json:"minimum_version"`
|
||||
// Deadline the required installation date for Nudge to enforce the required
|
||||
@@ -192,12 +199,12 @@ type MacOSUpdates struct {
|
||||
}
|
||||
|
||||
// Configured returns a boolean indicating if updates are configured
|
||||
func (m MacOSUpdates) Configured() bool {
|
||||
func (m AppleOSUpdateSettings) Configured() bool {
|
||||
return m.Deadline.Value != "" &&
|
||||
m.MinimumVersion.Value != ""
|
||||
}
|
||||
|
||||
func (m MacOSUpdates) Validate() error {
|
||||
func (m AppleOSUpdateSettings) Validate() error {
|
||||
// if no settings are provided it's okay to skip further validation
|
||||
if m.MinimumVersion.Value == "" && m.Deadline.Value == "" {
|
||||
// if one is set and empty, the other must be set and empty too, otherwise
|
||||
|
||||
+14
-16
@@ -13,26 +13,26 @@ func TestMacOSUpdatesValidate(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
m AppleOSUpdateSettings
|
||||
}{
|
||||
{"empty", MacOSUpdates{}},
|
||||
{"empty", AppleOSUpdateSettings{}},
|
||||
{
|
||||
"with full version",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"without patch version",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"only major version",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
@@ -49,25 +49,25 @@ func TestMacOSUpdatesValidate(t *testing.T) {
|
||||
t.Run("invalid deadline", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
m AppleOSUpdateSettings
|
||||
}{
|
||||
{
|
||||
"version but no deadline",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString(""),
|
||||
},
|
||||
},
|
||||
{
|
||||
"deadline with timestamp",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2020-01-01T00:00:00Z"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"incomplete date",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2020-01"),
|
||||
},
|
||||
@@ -84,25 +84,25 @@ func TestMacOSUpdatesValidate(t *testing.T) {
|
||||
t.Run("invalid version", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
m MacOSUpdates
|
||||
m AppleOSUpdateSettings
|
||||
}{
|
||||
{
|
||||
"deadline but no version",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString(""),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"version with build info",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0 (19A583)"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"version with patch info",
|
||||
MacOSUpdates{
|
||||
AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0-patch1"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
@@ -185,7 +185,7 @@ func TestMacOSUpdatesConfigured(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
m := MacOSUpdates{
|
||||
m := AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString(tc.version),
|
||||
Deadline: optjson.SetString(tc.deadline),
|
||||
}
|
||||
@@ -266,10 +266,8 @@ func TestAppConfigDeprecatedFields(t *testing.T) {
|
||||
diskEncryption, exists := mdm["enable_disk_encryption"]
|
||||
require.True(t, exists)
|
||||
require.EqualValues(t, c.wantDiskEncryption, diskEncryption)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAtLeastOnePlatformEnabledAndConfigured(t *testing.T) {
|
||||
|
||||
@@ -28,11 +28,16 @@ type UserCalendar interface {
|
||||
// CreateEvent, GetAndUpdateEvent and DeleteEvent reference the user's calendar.
|
||||
Configure(userEmail string) error
|
||||
// CreateEvent creates a new event on the calendar on the given date. DayEndedError is returned if there is no time left on the given date to schedule event.
|
||||
CreateEvent(dateOfEvent time.Time, genBodyFn func(conflict bool) (body string, ok bool, err error)) (event *CalendarEvent, err error)
|
||||
CreateEvent(
|
||||
dateOfEvent time.Time,
|
||||
genBodyFn func(conflict bool) (body string, ok bool, err error),
|
||||
opts CalendarCreateEventOpts,
|
||||
) (event *CalendarEvent, err error)
|
||||
// GetAndUpdateEvent retrieves the event from the calendar.
|
||||
// If the event has been modified, it returns the updated event.
|
||||
// If the event has been deleted, it schedules a new event with given body callback and returns the new event.
|
||||
GetAndUpdateEvent(event *CalendarEvent, genBodyFn func(conflict bool) (body string, ok bool, err error)) (updatedEvent *CalendarEvent,
|
||||
GetAndUpdateEvent(event *CalendarEvent, genBodyFn func(conflict bool) (body string, ok bool, err error),
|
||||
opts CalendarGetAndUpdateEventOpts) (updatedEvent *CalendarEvent,
|
||||
updated bool, err error)
|
||||
// DeleteEvent deletes the event with the given ID.
|
||||
DeleteEvent(event *CalendarEvent) error
|
||||
@@ -61,6 +66,16 @@ type Lock interface {
|
||||
GetSet(ctx context.Context, key string) ([]string, error)
|
||||
}
|
||||
|
||||
type CalendarCreateEventOpts struct {
|
||||
EventUUID string
|
||||
ChannelID string
|
||||
ResourceID string
|
||||
}
|
||||
|
||||
type CalendarGetAndUpdateEventOpts struct {
|
||||
UpdateTimezone bool
|
||||
}
|
||||
|
||||
type CalendarWebhookPayload struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
HostID uint `json:"host_id"`
|
||||
|
||||
@@ -723,3 +723,10 @@ type VPPTokenData struct {
|
||||
// structure of `VPPTokenRaw`.
|
||||
Token string `json:"token"`
|
||||
}
|
||||
type AppleDevice int
|
||||
|
||||
const (
|
||||
MacOS AppleDevice = iota
|
||||
IOS
|
||||
IPadOS
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ type nudgeUpdateElements struct {
|
||||
MainHeader string `json:"mainHeader"`
|
||||
}
|
||||
|
||||
func NewNudgeConfig(macOSUpdates MacOSUpdates) (*NudgeConfig, error) {
|
||||
func NewNudgeConfig(macOSUpdates AppleOSUpdateSettings) (*NudgeConfig, error) {
|
||||
deadline, err := time.Parse("2006-01-02", macOSUpdates.Deadline.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -34,7 +34,7 @@ type EnterpriseOverrides struct {
|
||||
DeleteMDMAppleBootstrapPackage func(ctx context.Context, teamID *uint) error
|
||||
MDMWindowsEnableOSUpdates func(ctx context.Context, teamID *uint, updates WindowsUpdates) error
|
||||
MDMWindowsDisableOSUpdates func(ctx context.Context, teamID *uint) error
|
||||
MDMAppleEditedMacOSUpdates func(ctx context.Context, teamID *uint, updates MacOSUpdates) error
|
||||
MDMAppleEditedAppleOSUpdates func(ctx context.Context, teamID *uint, appleDevice AppleDevice, updates AppleOSUpdateSettings) error
|
||||
}
|
||||
|
||||
type OsqueryService interface {
|
||||
|
||||
+26
-10
@@ -33,10 +33,18 @@ type TeamPayload struct {
|
||||
// need to be able which part of the MDM config was provided in the request,
|
||||
// so the fields are pointers to structs.
|
||||
type TeamPayloadMDM struct {
|
||||
EnableDiskEncryption optjson.Bool `json:"enable_disk_encryption"`
|
||||
MacOSUpdates *MacOSUpdates `json:"macos_updates"`
|
||||
WindowsUpdates *WindowsUpdates `json:"windows_updates"`
|
||||
MacOSSetup *MacOSSetup `json:"macos_setup"`
|
||||
EnableDiskEncryption optjson.Bool `json:"enable_disk_encryption"`
|
||||
|
||||
// MacOSUpdates defines the OS update settings for macOS devices.
|
||||
MacOSUpdates *AppleOSUpdateSettings `json:"macos_updates"`
|
||||
// IOSUpdates defines the OS update settings for iOS devices.
|
||||
IOSUpdates *AppleOSUpdateSettings `json:"ios_updates"`
|
||||
// IPadOSUpdates defines the OS update settings for iPadOS devices.
|
||||
IPadOSUpdates *AppleOSUpdateSettings `json:"ipados_updates"`
|
||||
// WindowsUpdates defines the OS update settings for Windows devices.
|
||||
WindowsUpdates *WindowsUpdates `json:"windows_updates"`
|
||||
|
||||
MacOSSetup *MacOSSetup `json:"macos_setup"`
|
||||
}
|
||||
|
||||
// Team is the data representation for the "Team" concept (group of hosts and
|
||||
@@ -178,11 +186,13 @@ type TeamSpecSoftwarePackage struct {
|
||||
}
|
||||
|
||||
type TeamMDM struct {
|
||||
EnableDiskEncryption bool `json:"enable_disk_encryption"`
|
||||
MacOSUpdates MacOSUpdates `json:"macos_updates"`
|
||||
WindowsUpdates WindowsUpdates `json:"windows_updates"`
|
||||
MacOSSettings MacOSSettings `json:"macos_settings"`
|
||||
MacOSSetup MacOSSetup `json:"macos_setup"`
|
||||
EnableDiskEncryption bool `json:"enable_disk_encryption"`
|
||||
MacOSUpdates AppleOSUpdateSettings `json:"macos_updates"`
|
||||
IOSUpdates AppleOSUpdateSettings `json:"ios_updates"`
|
||||
IPadOSUpdates AppleOSUpdateSettings `json:"ipados_updates"`
|
||||
WindowsUpdates WindowsUpdates `json:"windows_updates"`
|
||||
MacOSSettings MacOSSettings `json:"macos_settings"`
|
||||
MacOSSetup MacOSSetup `json:"macos_setup"`
|
||||
|
||||
WindowsSettings WindowsSettings `json:"windows_settings"`
|
||||
// NOTE: TeamSpecMDM must be kept in sync with TeamMDM.
|
||||
@@ -233,7 +243,13 @@ func (t *TeamMDM) Copy() *TeamMDM {
|
||||
type TeamSpecMDM struct {
|
||||
EnableDiskEncryption optjson.Bool `json:"enable_disk_encryption"`
|
||||
|
||||
MacOSUpdates MacOSUpdates `json:"macos_updates"`
|
||||
// MacOSUpdates defines the OS update settings for macOS devices.
|
||||
MacOSUpdates AppleOSUpdateSettings `json:"macos_updates"`
|
||||
// IOSUpdates defines the OS update settings for iOS devices.
|
||||
IOSUpdates AppleOSUpdateSettings `json:"ios_updates"`
|
||||
// IPadOSUpdates defines the OS update settings for iPadOS devices.
|
||||
IPadOSUpdates AppleOSUpdateSettings `json:"ipados_updates"`
|
||||
// WindowsUpdates defines the OS update settings for Windows devices.
|
||||
WindowsUpdates WindowsUpdates `json:"windows_updates"`
|
||||
|
||||
// A map is used for the macos settings so that we can easily detect if its
|
||||
|
||||
@@ -269,7 +269,7 @@ func TestTeamMDMCopy(t *testing.T) {
|
||||
t.Run("copy value fields", func(t *testing.T) {
|
||||
tm := &TeamMDM{
|
||||
EnableDiskEncryption: true,
|
||||
MacOSUpdates: MacOSUpdates{
|
||||
MacOSUpdates: AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.4"),
|
||||
Deadline: optjson.SetString("2020-01-01"),
|
||||
},
|
||||
|
||||
@@ -299,6 +299,33 @@ func (svc *MDMAppleCommander) DeviceConfigured(ctx context.Context, hostUUID, cm
|
||||
return svc.EnqueueCommand(ctx, []string{hostUUID}, raw)
|
||||
}
|
||||
|
||||
func (svc *MDMAppleCommander) DeviceInformation(ctx context.Context, hostUUIDs []string, cmdUUID string) error {
|
||||
raw := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Command</key>
|
||||
<dict>
|
||||
<key>Queries</key>
|
||||
<array>
|
||||
<string>DeviceName</string>
|
||||
<string>DeviceCapacity</string>
|
||||
<string>AvailableDeviceCapacity</string>
|
||||
<string>OSVersion</string>
|
||||
<string>WiFiMAC</string>
|
||||
<string>ProductName</string>
|
||||
</array>
|
||||
<key>RequestType</key>
|
||||
<string>DeviceInformation</string>
|
||||
</dict>
|
||||
<key>CommandUUID</key>
|
||||
<string>%s</string>
|
||||
</dict>
|
||||
</plist>`, cmdUUID)
|
||||
|
||||
return svc.EnqueueCommand(ctx, hostUUIDs, raw)
|
||||
}
|
||||
|
||||
// EnqueueCommand takes care of enqueuing the commands and sending push
|
||||
// notifications to the devices.
|
||||
//
|
||||
|
||||
+15
-1
@@ -101,6 +101,14 @@ const (
|
||||
// FleetMacOSUpdatesProfileName is the name of the DDM profile used by Fleet
|
||||
// to configure macOS OS updates.
|
||||
FleetMacOSUpdatesProfileName = "Fleet macOS OS Updates"
|
||||
|
||||
// FleetIOSUpdatesProfileName is the name of the DDM profile used by Fleet
|
||||
// to configure iOS OS updates.
|
||||
FleetIOSUpdatesProfileName = "Fleet iOS OS Updates"
|
||||
|
||||
// FleetIPadOSUpdatesProfileName is the name of the DDM profile used by Fleet
|
||||
// to configure iPadOS OS updates.
|
||||
FleetIPadOSUpdatesProfileName = "Fleet iPadOS OS Updates"
|
||||
)
|
||||
|
||||
// FleetReservedProfileNames returns a map of PayloadDisplayName or profile
|
||||
@@ -111,6 +119,8 @@ func FleetReservedProfileNames() map[string]struct{} {
|
||||
FleetFileVaultProfileName: {},
|
||||
FleetWindowsOSUpdatesProfileName: {},
|
||||
FleetMacOSUpdatesProfileName: {},
|
||||
FleetIOSUpdatesProfileName: {},
|
||||
FleetIPadOSUpdatesProfileName: {},
|
||||
FleetCAConfigProfileName: {},
|
||||
}
|
||||
}
|
||||
@@ -130,5 +140,9 @@ func ListFleetReservedMacOSProfileNames() []string {
|
||||
// ListFleetReservedMacOSDeclarationNames returns a list of declaration names
|
||||
// that are reserved by Fleet for Apple DDM declarations.
|
||||
func ListFleetReservedMacOSDeclarationNames() []string {
|
||||
return []string{FleetMacOSUpdatesProfileName}
|
||||
return []string{
|
||||
FleetMacOSUpdatesProfileName,
|
||||
FleetIOSUpdatesProfileName,
|
||||
FleetIPadOSUpdatesProfileName,
|
||||
}
|
||||
}
|
||||
|
||||
+82
-24
@@ -558,27 +558,26 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
}
|
||||
}
|
||||
|
||||
// if the macOS minimum version requirement changed, create the corresponding
|
||||
// activity
|
||||
if oldAppConfig.MDM.MacOSUpdates.MinimumVersion.Value != appConfig.MDM.MacOSUpdates.MinimumVersion.Value ||
|
||||
oldAppConfig.MDM.MacOSUpdates.Deadline.Value != appConfig.MDM.MacOSUpdates.Deadline.Value {
|
||||
if license.IsPremium() {
|
||||
// macOS updates are premium feature
|
||||
if err := svc.EnterpriseOverrides.MDMAppleEditedMacOSUpdates(ctx, nil, appConfig.MDM.MacOSUpdates); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "update DDM profile after macOS updates change")
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.NewActivity(
|
||||
ctx,
|
||||
authz.UserFromContext(ctx),
|
||||
fleet.ActivityTypeEditedMacOSMinVersion{
|
||||
MinimumVersion: appConfig.MDM.MacOSUpdates.MinimumVersion.Value,
|
||||
Deadline: appConfig.MDM.MacOSUpdates.Deadline.Value,
|
||||
},
|
||||
); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "create activity for app config macos min version modification")
|
||||
}
|
||||
//
|
||||
// Process OS updates config changes for Apple devices.
|
||||
//
|
||||
if err := svc.processAppleOSUpdateSettings(ctx, license, fleet.MacOS,
|
||||
oldAppConfig.MDM.MacOSUpdates,
|
||||
appConfig.MDM.MacOSUpdates,
|
||||
); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "process macOS OS updates config change")
|
||||
}
|
||||
if err := svc.processAppleOSUpdateSettings(ctx, license, fleet.IOS,
|
||||
oldAppConfig.MDM.IOSUpdates,
|
||||
appConfig.MDM.IOSUpdates,
|
||||
); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "process iOS OS updates config change")
|
||||
}
|
||||
if err := svc.processAppleOSUpdateSettings(ctx, license, fleet.IPadOS,
|
||||
oldAppConfig.MDM.IPadOSUpdates,
|
||||
appConfig.MDM.IPadOSUpdates,
|
||||
); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "process iPadOS OS updates config change")
|
||||
}
|
||||
|
||||
// if the Windows updates requirements changed, create the corresponding
|
||||
@@ -670,6 +669,47 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
||||
return obfuscatedAppConfig, nil
|
||||
}
|
||||
|
||||
// processAppleOSUpdateSettings updates the OS updates configuration if the minimum version+deadline are updated.
|
||||
func (svc *Service) processAppleOSUpdateSettings(
|
||||
ctx context.Context,
|
||||
license *fleet.LicenseInfo,
|
||||
appleDevice fleet.AppleDevice,
|
||||
oldOSUpdateSettings fleet.AppleOSUpdateSettings,
|
||||
newOSUpdateSettings fleet.AppleOSUpdateSettings,
|
||||
) error {
|
||||
if oldOSUpdateSettings.MinimumVersion.Value != newOSUpdateSettings.MinimumVersion.Value ||
|
||||
oldOSUpdateSettings.Deadline.Value != newOSUpdateSettings.Deadline.Value {
|
||||
if license.IsPremium() {
|
||||
if err := svc.EnterpriseOverrides.MDMAppleEditedAppleOSUpdates(ctx, nil, appleDevice, newOSUpdateSettings); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "update DDM profile after Apple OS updates change")
|
||||
}
|
||||
}
|
||||
|
||||
var activity fleet.ActivityDetails
|
||||
switch appleDevice {
|
||||
case fleet.MacOS:
|
||||
activity = fleet.ActivityTypeEditedMacOSMinVersion{
|
||||
MinimumVersion: newOSUpdateSettings.MinimumVersion.Value,
|
||||
Deadline: newOSUpdateSettings.Deadline.Value,
|
||||
}
|
||||
case fleet.IOS:
|
||||
activity = fleet.ActivityTypeEditedIOSMinVersion{
|
||||
MinimumVersion: newOSUpdateSettings.MinimumVersion.Value,
|
||||
Deadline: newOSUpdateSettings.Deadline.Value,
|
||||
}
|
||||
case fleet.IPadOS:
|
||||
activity = fleet.ActivityTypeEditedIPadOSMinVersion{
|
||||
MinimumVersion: newOSUpdateSettings.MinimumVersion.Value,
|
||||
Deadline: newOSUpdateSettings.Deadline.Value,
|
||||
}
|
||||
}
|
||||
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), activity); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "create activity for app config apple min version modification")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) HasCustomSetupAssistantConfigurationWebURL(ctx context.Context, teamID *uint) (bool, error) {
|
||||
az, ok := authz_ctx.FromContext(ctx)
|
||||
if !ok || !az.Checked() {
|
||||
@@ -784,12 +824,24 @@ func (svc *Service) validateMDM(
|
||||
}
|
||||
|
||||
// MacOSUpdates
|
||||
updatingVersion := mdm.MacOSUpdates.MinimumVersion.Value != "" &&
|
||||
updatingMacOSVersion := mdm.MacOSUpdates.MinimumVersion.Value != "" &&
|
||||
mdm.MacOSUpdates.MinimumVersion != oldMdm.MacOSUpdates.MinimumVersion
|
||||
updatingDeadline := mdm.MacOSUpdates.Deadline.Value != "" &&
|
||||
updatingMacOSDeadline := mdm.MacOSUpdates.Deadline.Value != "" &&
|
||||
mdm.MacOSUpdates.Deadline != oldMdm.MacOSUpdates.Deadline
|
||||
// IOSUpdates
|
||||
updatingIOSVersion := mdm.IOSUpdates.MinimumVersion.Value != "" &&
|
||||
mdm.IOSUpdates.MinimumVersion != oldMdm.IOSUpdates.MinimumVersion
|
||||
updatingIOSDeadline := mdm.IOSUpdates.Deadline.Value != "" &&
|
||||
mdm.IOSUpdates.Deadline != oldMdm.IOSUpdates.Deadline
|
||||
// IPadOSUpdates
|
||||
updatingIPadOSVersion := mdm.IPadOSUpdates.MinimumVersion.Value != "" &&
|
||||
mdm.IPadOSUpdates.MinimumVersion != oldMdm.IPadOSUpdates.MinimumVersion
|
||||
updatingIPadOSDeadline := mdm.IPadOSUpdates.Deadline.Value != "" &&
|
||||
mdm.IPadOSUpdates.Deadline != oldMdm.IPadOSUpdates.Deadline
|
||||
|
||||
if updatingVersion || updatingDeadline {
|
||||
if updatingMacOSVersion || updatingMacOSDeadline ||
|
||||
updatingIOSVersion || updatingIOSDeadline ||
|
||||
updatingIPadOSVersion || updatingIPadOSDeadline {
|
||||
// TODO: Should we validate MDM configured on here too?
|
||||
|
||||
if !license.IsPremium() {
|
||||
@@ -800,6 +852,12 @@ func (svc *Service) validateMDM(
|
||||
if err := mdm.MacOSUpdates.Validate(); err != nil {
|
||||
invalid.Append("macos_updates", err.Error())
|
||||
}
|
||||
if err := mdm.IOSUpdates.Validate(); err != nil {
|
||||
invalid.Append("ios_updates", err.Error())
|
||||
}
|
||||
if err := mdm.IPadOSUpdates.Validate(); err != nil {
|
||||
invalid.Append("ipados_updates", err.Error())
|
||||
}
|
||||
|
||||
// WindowsUpdates
|
||||
updatingWindowsUpdates := !mdm.WindowsUpdates.Equal(oldMdm.WindowsUpdates)
|
||||
|
||||
@@ -859,7 +859,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
licenseTier: "free",
|
||||
expectedMDM: fleet.MDM{
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -889,7 +891,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBMDefaultTeam: "foobar",
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -904,7 +908,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
AppleBMDefaultTeam: "foobar",
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -925,7 +931,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
EndUserAuthentication: fleet.MDMEndUserAuthentication{SSOProviderSettings: fleet.SSOProviderSettings{EntityID: "foo"}},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -949,7 +957,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
IDPName: "onelogin",
|
||||
}},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
@@ -1007,7 +1017,9 @@ func TestMDMAppleConfig(t *testing.T) {
|
||||
expectedMDM: fleet.MDM{
|
||||
EnableDiskEncryption: optjson.Bool{Set: true, Valid: true, Value: false},
|
||||
MacOSSetup: fleet.MacOSSetup{BootstrapPackage: optjson.String{Set: true}, MacOSSetupAssistant: optjson.String{Set: true}, EnableReleaseDeviceManually: optjson.SetBool(false)},
|
||||
MacOSUpdates: fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}},
|
||||
WindowsUpdates: fleet.WindowsUpdates{DeadlineDays: optjson.Int{Set: true}, GracePeriodDays: optjson.Int{Set: true}},
|
||||
WindowsSettings: fleet.WindowsSettings{
|
||||
CustomSettings: optjson.Slice[fleet.MDMProfileSpec]{Set: true, Value: []fleet.MDMProfileSpec{}},
|
||||
|
||||
@@ -2752,6 +2752,7 @@ func (svc *MDMAppleCheckinAndCommandService) CommandAndReportResults(r *mdm.Requ
|
||||
host.PrimaryMac = wifiMac
|
||||
host.HardwareModel = productName
|
||||
host.DetailUpdatedAt = time.Now()
|
||||
host.RefetchRequested = false
|
||||
if err := svc.ds.UpdateHost(r.Context, host); err != nil {
|
||||
return nil, ctxerr.Wrap(r.Context, err, "failed to update host")
|
||||
}
|
||||
@@ -2805,7 +2806,7 @@ func (svc *MDMAppleCheckinAndCommandService) CommandAndReportResults(r *mdm.Requ
|
||||
// set "pending-install" profiles to "verifying" or "failed"
|
||||
// depending on the status of the DeviceManagement command
|
||||
status := mdmAppleDeliveryStatusFromCommandStatus(cmdResult.Status)
|
||||
detail := fmt.Sprintf("%s. Make sure the host is on macOS 13 or higher.", apple_mdm.FmtErrorChain(cmdResult.ErrorChain))
|
||||
detail := fmt.Sprintf("%s. Make sure the host is on macOS 13+, iOS 17+, iPadOS 17+.", apple_mdm.FmtErrorChain(cmdResult.ErrorChain))
|
||||
err := svc.ds.MDMAppleSetPendingDeclarationsAs(r.Context, cmdResult.UDID, status, detail)
|
||||
return nil, ctxerr.Wrap(r.Context, err, "update declaration status on DeclarativeManagement ack")
|
||||
case "InstallApplication":
|
||||
@@ -3276,7 +3277,7 @@ func ReconcileAppleProfiles(
|
||||
|
||||
// scepCertRenewalThresholdDays defines the number of days before a SCEP
|
||||
// certificate must be renewed.
|
||||
const scepCertRenewalThresholdDays = 30
|
||||
const scepCertRenewalThresholdDays = 180
|
||||
|
||||
// maxCertsRenewalPerRun specifies the maximum number of certificates to renew
|
||||
// in a single cron run.
|
||||
@@ -3285,8 +3286,8 @@ const scepCertRenewalThresholdDays = 30
|
||||
// day, and we have room for 24,000 * scepCertRenewalThresholdDays total
|
||||
// renewals.
|
||||
//
|
||||
// For a default of 30 days as a threshold this gives us room for a fleet of
|
||||
// 720,000 devices expiring at the same time.
|
||||
// For a default of 180 days as a threshold this gives us room for a fleet of
|
||||
// ~4 million devices expiring at the same time.
|
||||
const maxCertsRenewalPerRun = 100
|
||||
|
||||
func RenewSCEPCertificates(
|
||||
|
||||
+37
-35
@@ -2,53 +2,55 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// Disabling the calendarWebhookEndpoint to address bugs
|
||||
|
||||
// type calendarWebhookRequest struct {
|
||||
// eventUUID string
|
||||
// googleChannelID string
|
||||
// googleResourceState string
|
||||
// }
|
||||
type calendarWebhookRequest struct {
|
||||
eventUUID string
|
||||
googleChannelID string
|
||||
googleResourceState string
|
||||
}
|
||||
|
||||
// DecodeRequest implement requestDecoder interface to take full control of decoding the request
|
||||
// func (calendarWebhookRequest) DecodeRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
// var req calendarWebhookRequest
|
||||
// eventUUID, ok := mux.Vars(r)["event_uuid"]
|
||||
// if !ok {
|
||||
// return nil, errBadRoute
|
||||
// }
|
||||
// unescaped, err := url.PathUnescape(eventUUID)
|
||||
// if err != nil {
|
||||
// return "", ctxerr.Wrap(r.Context(), err, "unescape value in path")
|
||||
// }
|
||||
// req.eventUUID = unescaped
|
||||
func (calendarWebhookRequest) DecodeRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
var req calendarWebhookRequest
|
||||
eventUUID, ok := mux.Vars(r)["event_uuid"]
|
||||
if !ok {
|
||||
return nil, errBadRoute
|
||||
}
|
||||
unescaped, err := url.PathUnescape(eventUUID)
|
||||
if err != nil {
|
||||
return "", ctxerr.Wrap(r.Context(), err, "unescape value in path")
|
||||
}
|
||||
req.eventUUID = unescaped
|
||||
|
||||
// req.googleChannelID = r.Header.Get("X-Goog-Channel-Id")
|
||||
// req.googleResourceState = r.Header.Get("X-Goog-Resource-State")
|
||||
req.googleChannelID = r.Header.Get("X-Goog-Channel-Id")
|
||||
req.googleResourceState = r.Header.Get("X-Goog-Resource-State")
|
||||
|
||||
// return &req, nil
|
||||
// }
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
// type calendarWebhookResponse struct {
|
||||
// Err error `json:"error,omitempty"`
|
||||
// }
|
||||
type calendarWebhookResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// func (r calendarWebhookResponse) error() error { return r.Err }
|
||||
func (r calendarWebhookResponse) error() error { return r.Err }
|
||||
|
||||
// func calendarWebhookEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
// req := request.(*calendarWebhookRequest)
|
||||
// err := svc.CalendarWebhook(ctx, req.eventUUID, req.googleChannelID, req.googleResourceState)
|
||||
// if err != nil {
|
||||
// return calendarWebhookResponse{Err: err}, err
|
||||
// }
|
||||
func calendarWebhookEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
req := request.(*calendarWebhookRequest)
|
||||
err := svc.CalendarWebhook(ctx, req.eventUUID, req.googleChannelID, req.googleResourceState)
|
||||
if err != nil {
|
||||
return calendarWebhookResponse{Err: err}, err
|
||||
}
|
||||
|
||||
// resp := calendarWebhookResponse{}
|
||||
// return resp, nil
|
||||
// }
|
||||
resp := calendarWebhookResponse{}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (svc *Service) CalendarWebhook(ctx context.Context, eventUUID string, channelID string, resourceState string) error {
|
||||
// skipauth: No authorization check needed due to implementation returning only license error.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/ee/server/calendar"
|
||||
"github.com/fleetdm/fleet/v4/server/config"
|
||||
@@ -19,9 +20,28 @@ import (
|
||||
const (
|
||||
LockKeyPrefix = "calendar:lock:"
|
||||
ReservedLockKeyPrefix = "calendar:reserved:"
|
||||
RecentUpdateKeyPrefix = "calendar:recent_update:"
|
||||
QueueKey = "calendar:queue"
|
||||
|
||||
// DistributedLockExpireMs is the time Redis will hold the lock before automatically releasing it.
|
||||
// Our current max retry time for calendar API is 10 minutes, and multiple API calls (with their own retry timing) can be made during event processing.
|
||||
// If a Fleet server gets the lock and is then shut down before releasing the lock, the next server may need to wait this long
|
||||
// before getting the lock.
|
||||
DistributedLockExpireMs = 20 * 60 * 1000
|
||||
// ReserveLockExpireMs is used by cron job to guarantee that it gets the next lock.
|
||||
ReserveLockExpireMs = 2 * DistributedLockExpireMs
|
||||
|
||||
// RecentCalendarUpdateValue is the value stored in Redis to indicate that a calendar event was recently updated.
|
||||
RecentCalendarUpdateValue = "1"
|
||||
)
|
||||
|
||||
// RecentCalendarUpdateDuration is the duration during which we will ignore a calendar event callback if the event in DB was just updated by a previous callback.
|
||||
// This reduces CPU load and Google API load. If we update the event, Google calendar may send a callback which we don't need to process.
|
||||
// We are using Redis instead of updated_at timestamp in DB because the calendar cron job may update the timestamp even when the event did not change, which could
|
||||
// cause us to miss a legitimate update.
|
||||
// This variable is exposed so that it can be modified by unit tests.
|
||||
var RecentCalendarUpdateDuration = 10 * time.Second
|
||||
|
||||
type Config struct {
|
||||
config.CalendarConfig
|
||||
fleet.GoogleCalendarIntegration
|
||||
|
||||
@@ -1298,6 +1298,32 @@ func (c *Client) DoGitOps(
|
||||
if deadline, ok := macOSUpdates["deadline"]; !ok || deadline == nil {
|
||||
macOSUpdates["deadline"] = ""
|
||||
}
|
||||
// Put in default values for ios_updates
|
||||
if config.Controls.IOSUpdates != nil {
|
||||
mdmAppConfig["ios_updates"] = config.Controls.IOSUpdates
|
||||
} else {
|
||||
mdmAppConfig["ios_updates"] = map[string]interface{}{}
|
||||
}
|
||||
iOSUpdates := mdmAppConfig["ios_updates"].(map[string]interface{})
|
||||
if minimumVersion, ok := iOSUpdates["minimum_version"]; !ok || minimumVersion == nil {
|
||||
iOSUpdates["minimum_version"] = ""
|
||||
}
|
||||
if deadline, ok := iOSUpdates["deadline"]; !ok || deadline == nil {
|
||||
iOSUpdates["deadline"] = ""
|
||||
}
|
||||
// Put in default values for ipados_updates
|
||||
if config.Controls.IPadOSUpdates != nil {
|
||||
mdmAppConfig["ipados_updates"] = config.Controls.IPadOSUpdates
|
||||
} else {
|
||||
mdmAppConfig["ipados_updates"] = map[string]interface{}{}
|
||||
}
|
||||
iPadOSUpdates := mdmAppConfig["ipados_updates"].(map[string]interface{})
|
||||
if minimumVersion, ok := iPadOSUpdates["minimum_version"]; !ok || minimumVersion == nil {
|
||||
iPadOSUpdates["minimum_version"] = ""
|
||||
}
|
||||
if deadline, ok := iPadOSUpdates["deadline"]; !ok || deadline == nil {
|
||||
iPadOSUpdates["deadline"] = ""
|
||||
}
|
||||
// Put in default values for macos_setup
|
||||
if config.Controls.MacOSSetup != nil {
|
||||
mdmAppConfig["macos_setup"] = config.Controls.MacOSSetup
|
||||
|
||||
@@ -950,8 +950,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ne.HEAD("/api/fleet/orbit/ping", orbitPingEndpoint, orbitPingRequest{})
|
||||
|
||||
// This is a callback endpoint for calendar integration -- it is called to notify an event change in a user calendar
|
||||
// Disabling the calendarWebhookEndpoint to address bugs
|
||||
// ne.POST("/api/_version_/fleet/calendar/webhook/{event_uuid}", calendarWebhookEndpoint, calendarWebhookRequest{})
|
||||
ne.POST("/api/_version_/fleet/calendar/webhook/{event_uuid}", calendarWebhookEndpoint, calendarWebhookRequest{})
|
||||
|
||||
neAppleMDM.WithCustomMiddleware(limiter.Limit("login", throttled.RateQuota{MaxRate: loginRateLimit, MaxBurst: 9})).
|
||||
POST("/api/_version_/fleet/mdm/sso", initiateMDMAppleSSOEndpoint, initiateMDMAppleSSORequest{})
|
||||
|
||||
+36
-2
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
"github.com/fleetdm/fleet/v4/server/worker"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/gocarina/gocsv"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// HostDetailResponse is the response struct that contains the full host information
|
||||
@@ -1008,12 +1010,15 @@ func refetchHostEndpoint(ctx context.Context, request interface{}, svc fleet.Ser
|
||||
}
|
||||
|
||||
func (svc *Service) RefetchHost(ctx context.Context, id uint) error {
|
||||
var host *fleet.Host
|
||||
// iOS and iPadOS refetch are not authenticated with device token because these devices do not have Fleet Desktop
|
||||
if !svc.authz.IsAuthenticatedWith(ctx, authzctx.AuthnDeviceToken) {
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil {
|
||||
var err error
|
||||
if err = svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := svc.ds.HostLite(ctx, id)
|
||||
host, err = svc.ds.HostLite(ctx, id)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "find host for refetch")
|
||||
}
|
||||
@@ -1025,6 +1030,17 @@ func (svc *Service) RefetchHost(ctx context.Context, id uint) error {
|
||||
}
|
||||
}
|
||||
|
||||
if host != nil && (host.Platform == "ios" || host.Platform == "ipados") {
|
||||
err := svc.verifyMDMConfiguredAndConnected(ctx, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = svc.mdmAppleCommander.DeviceInformation(ctx, []string{host.UUID}, fleet.RefetchCommandUUIDPrefix+uuid.NewString())
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "refetch host with MDM")
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.ds.UpdateHostRefetchRequested(ctx, id, true); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "save host")
|
||||
}
|
||||
@@ -1032,6 +1048,24 @@ func (svc *Service) RefetchHost(ctx context.Context, id uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) verifyMDMConfiguredAndConnected(ctx context.Context, host *fleet.Host) error {
|
||||
if err := svc.VerifyMDMAppleConfigured(ctx); err != nil {
|
||||
if errors.Is(err, fleet.ErrMDMNotConfigured) {
|
||||
err = fleet.NewInvalidArgumentError("id", fleet.AppleMDMNotConfiguredMessage).WithStatus(http.StatusBadRequest)
|
||||
}
|
||||
return ctxerr.Wrap(ctx, err, "check macOS MDM enabled")
|
||||
}
|
||||
connected, err := svc.ds.IsHostConnectedToFleetMDM(ctx, host)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "checking if host is connected to Fleet")
|
||||
}
|
||||
if !connected {
|
||||
return ctxerr.Wrap(ctx,
|
||||
fleet.NewInvalidArgumentError("id", "Host does not have MDM turned on."))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) getHostDetails(ctx context.Context, host *fleet.Host, opts fleet.HostDetailOptions) (*fleet.HostDetail, error) {
|
||||
if !opts.ExcludeSoftware {
|
||||
if err := svc.ds.LoadHostSoftware(ctx, host, opts.IncludeCVEScores); err != nil {
|
||||
|
||||
@@ -169,6 +169,14 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
"minimum_version": "10.15.0",
|
||||
"deadline": "2021-01-01",
|
||||
},
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "17.5.1",
|
||||
"deadline": "2024-07-23",
|
||||
},
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "18.0",
|
||||
"deadline": "2024-08-24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -188,10 +196,18 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
AdditionalQueries: ptr.RawMessage(json.RawMessage(`{"foo": "bar"}`)),
|
||||
}, team.Config.Features)
|
||||
require.Equal(t, fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("17.5.1"),
|
||||
Deadline: optjson.SetString("2024-07-23"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("18.0"),
|
||||
Deadline: optjson.SetString("2024-08-24"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.Int{Set: true},
|
||||
GracePeriodDays: optjson.Int{Set: true},
|
||||
@@ -285,10 +301,18 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, applyResp.TeamIDsByName[teamName], team.ID)
|
||||
require.Equal(t, fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("17.5.1"),
|
||||
Deadline: optjson.SetString("2024-07-23"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("18.0"),
|
||||
Deadline: optjson.SetString("2024-08-24"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.SetInt(1),
|
||||
GracePeriodDays: optjson.SetInt(1),
|
||||
@@ -307,10 +331,18 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
var getTmResp getTeamResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/teams/"+fmt.Sprint(team.ID), nil, http.StatusOK, &getTmResp)
|
||||
require.Equal(t, fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("17.5.1"),
|
||||
Deadline: optjson.SetString("2024-07-23"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("18.0"),
|
||||
Deadline: optjson.SetString("2024-08-24"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.SetInt(1),
|
||||
GracePeriodDays: optjson.SetInt(1),
|
||||
@@ -331,10 +363,18 @@ func (s *integrationEnterpriseTestSuite) TestTeamSpecs() {
|
||||
require.True(t, len(listTmResp.Teams) > 0)
|
||||
require.Equal(t, team.ID, listTmResp.Teams[0].ID)
|
||||
require.Equal(t, fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("17.5.1"),
|
||||
Deadline: optjson.SetString("2024-07-23"),
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("18.0"),
|
||||
Deadline: optjson.SetString("2024-08-24"),
|
||||
},
|
||||
WindowsUpdates: fleet.WindowsUpdates{
|
||||
DeadlineDays: optjson.SetInt(1),
|
||||
GracePeriodDays: optjson.SetInt(1),
|
||||
@@ -2133,7 +2173,15 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
|
||||
var getTmResp getTeamResponse
|
||||
s.DoJSON("GET", "/api/latest/fleet/teams/"+fmt.Sprint(team.ID), nil, http.StatusOK, &getTmResp)
|
||||
require.Equal(t, fleet.TeamMDM{
|
||||
MacOSUpdates: fleet.MacOSUpdates{
|
||||
MacOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.String{Set: true},
|
||||
Deadline: optjson.String{Set: true},
|
||||
},
|
||||
IOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.String{Set: true},
|
||||
Deadline: optjson.String{Set: true},
|
||||
},
|
||||
IPadOSUpdates: fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.String{Set: true},
|
||||
Deadline: optjson.String{Set: true},
|
||||
},
|
||||
@@ -2173,7 +2221,7 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
|
||||
tmResp = teamResponse{}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"macos_updates": &fleet.MacOSUpdates{
|
||||
"macos_updates": &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
},
|
||||
@@ -2279,7 +2327,7 @@ func (s *integrationEnterpriseTestSuite) TestWindowsUpdatesTeamConfig() {
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) assertMacOSUpdatesDeclaration(teamID *uint, expected *fleet.MacOSUpdates) {
|
||||
func (s *integrationEnterpriseTestSuite) assertAppleOSUpdatesDeclaration(teamID *uint, profileName string, expected *fleet.AppleOSUpdateSettings) {
|
||||
t := s.T()
|
||||
if teamID == nil {
|
||||
teamID = ptr.Uint(0)
|
||||
@@ -2288,7 +2336,7 @@ func (s *integrationEnterpriseTestSuite) assertMacOSUpdatesDeclaration(teamID *u
|
||||
var declUUID string
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error {
|
||||
err := sqlx.GetContext(context.Background(), q, &declUUID,
|
||||
`SELECT declaration_uuid FROM mdm_apple_declarations WHERE team_id = ? AND name = ?`, teamID, mdm.FleetMacOSUpdatesProfileName)
|
||||
`SELECT declaration_uuid FROM mdm_apple_declarations WHERE team_id = ? AND name = ?`, teamID, profileName)
|
||||
if expected == nil {
|
||||
require.Error(t, err)
|
||||
return nil
|
||||
@@ -2307,10 +2355,9 @@ func (s *integrationEnterpriseTestSuite) assertMacOSUpdatesDeclaration(teamID *u
|
||||
require.Contains(t, string(decl.RawJSON), fmt.Sprintf(`"TargetLocalDateTime": "%sT12:00:00"`, expected.Deadline.Value))
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
func (s *integrationEnterpriseTestSuite) TestAppleOSUpdatesTeamConfig() {
|
||||
t := s.T()
|
||||
|
||||
// Create a team
|
||||
team := &fleet.Team{
|
||||
Name: t.Name(),
|
||||
Description: "Team description",
|
||||
@@ -2322,41 +2369,92 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
team.ID = tmResp.Team.ID
|
||||
|
||||
// no OS updates settings at the moment
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, nil)
|
||||
|
||||
// modify the team's config
|
||||
updates := &fleet.MacOSUpdates{
|
||||
// modify the team's config (macOS first)
|
||||
macOSUpdates := &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2021-01-01"),
|
||||
}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"macos_updates": updates,
|
||||
"macos_updates": macOSUpdates,
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2021-01-01"}`, team.ID, team.Name), 0)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, updates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, nil)
|
||||
|
||||
// only update the deadline
|
||||
updates = &fleet.MacOSUpdates{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2025-10-01"),
|
||||
// modify the team's config (now iOS and iPadOS)
|
||||
iOSUpdates := &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("11.11.11"),
|
||||
Deadline: optjson.SetString("2022-02-02"),
|
||||
}
|
||||
iPadOSUpdates := &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.12.12"),
|
||||
Deadline: optjson.SetString("2023-03-03"),
|
||||
}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"macos_updates": updates,
|
||||
"ios_updates": iOSUpdates,
|
||||
"ipados_updates": iPadOSUpdates,
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2021-01-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2022-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2023-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2021-01-01"}`, team.ID, team.Name), 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "11.11.11", "deadline": "2022-02-02"}`, team.ID, team.Name), 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "12.12.12", "deadline": "2023-03-03"}`, team.ID, team.Name), 0)
|
||||
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, iPadOSUpdates)
|
||||
|
||||
// only update the deadlines
|
||||
macOSUpdates = &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("10.15.0"),
|
||||
Deadline: optjson.SetString("2025-10-01"),
|
||||
}
|
||||
iOSUpdates = &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("11.11.11"),
|
||||
Deadline: optjson.SetString("2024-02-02"),
|
||||
}
|
||||
iPadOSUpdates = &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.12.12"),
|
||||
Deadline: optjson.SetString("2024-03-03"),
|
||||
}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"macos_updates": macOSUpdates,
|
||||
"ios_updates": iOSUpdates,
|
||||
"ipados_updates": iPadOSUpdates,
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
lastActivity := s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2025-10-01"}`, team.ID, team.Name), 0)
|
||||
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
|
||||
macOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "10.15.0", "deadline": "2025-10-01"}`, team.ID, team.Name), 0)
|
||||
iOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "11.11.11", "deadline": "2024-02-02"}`, team.ID, team.Name), 0)
|
||||
iPadOSLastActivity := s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "12.12.12", "deadline": "2024-03-03"}`, team.ID, team.Name), 0)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, updates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, iPadOSUpdates)
|
||||
|
||||
// setting the windows updates doesn't alter the macos updates
|
||||
// setting the windows updates doesn't alter the apple updates
|
||||
tmResp = teamResponse{}
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
@@ -2368,13 +2466,21 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
|
||||
require.Equal(t, 10, tmResp.Team.Config.MDM.WindowsUpdates.DeadlineDays.Value)
|
||||
require.Equal(t, 2, tmResp.Team.Config.MDM.WindowsUpdates.GracePeriodDays.Value)
|
||||
// did not create a new activity for macos updates
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), "", lastActivity)
|
||||
lastActivity = s.lastActivityMatches(fleet.ActivityTypeEditedWindowsUpdates{}.ActivityName(), ``, 0)
|
||||
// did not create a new activity for os updates
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), "", macOSLastActivity)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), "", iOSLastActivity)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), "", iPadOSLastActivity)
|
||||
lastActivity := s.lastActivityMatches(fleet.ActivityTypeEditedWindowsUpdates{}.ActivityName(), ``, 0)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, updates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, iPadOSUpdates)
|
||||
|
||||
// sending a nil MDM or MacOSUpdate config doesn't modify anything
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2387,10 +2493,16 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Equal(t, "10.15.0", tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2025-10-01", tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "11.11.11", tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-02-02", tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "12.12.12", tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-03-03", tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
|
||||
// no new activity is created
|
||||
s.lastActivityMatches("", "", lastActivity)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, updates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, iPadOSUpdates)
|
||||
|
||||
// sending macos settings but no macos_updates does not change the macos updates
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2405,22 +2517,40 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
// no new activity is created
|
||||
s.lastActivityMatches("", "", lastActivity)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, updates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, macOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, iOSUpdates)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, iPadOSUpdates)
|
||||
|
||||
// sending empty MacOSUpdate fields empties both fields
|
||||
// sending empty apple os updates fields empties both fields and removes the DDM profiles
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"macos_updates": map[string]any{
|
||||
"minimum_version": "",
|
||||
"deadline": nil,
|
||||
},
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "",
|
||||
"deadline": nil,
|
||||
},
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "",
|
||||
"deadline": nil,
|
||||
},
|
||||
},
|
||||
}, http.StatusOK, &tmResp)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.MacOSUpdates.Deadline.Value)
|
||||
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, tmResp.Team.Config.MDM.IPadOSUpdates.Deadline.Value)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), fmt.Sprintf(`{"team_id": %d, "team_name": %q, "minimum_version": "", "deadline": ""}`, team.ID, team.Name), 0)
|
||||
|
||||
s.assertMacOSUpdatesDeclaration(&team.ID, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetMacOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(&team.ID, mdm.FleetIPadOSUpdatesProfileName, nil)
|
||||
|
||||
// error checks:
|
||||
|
||||
@@ -2433,6 +2563,22 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "10.15.0",
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "10.15.0",
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set an invalid minimum version
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2443,6 +2589,22 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "10.15.0 (19A583)",
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "10.15.0 (19A583)",
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set a deadline but not a minimum version
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2452,6 +2614,20 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"deadline": "2021-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set an empty deadline but not a minimum version
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2461,6 +2637,20 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"deadline": "",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"deadline": "",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set a minimum version but not a deadline
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2470,6 +2660,20 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "10.15.0 (19A583)",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "10.15.0 (19A583)",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
|
||||
// try to set an empty minimum version but not a deadline
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
@@ -2479,6 +2683,20 @@ func (s *integrationEnterpriseTestSuite) TestMacOSUpdatesTeamConfig() {
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ios_updates": map[string]any{
|
||||
"minimum_version": "",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), map[string]any{
|
||||
"mdm": map[string]any{
|
||||
"ipados_updates": map[string]any{
|
||||
"minimum_version": "",
|
||||
},
|
||||
},
|
||||
}, http.StatusUnprocessableEntity, &tmResp)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestListDevicePolicies() {
|
||||
@@ -2868,7 +3086,7 @@ func (s *integrationEnterpriseTestSuite) TestMDMWindowsUpdates() {
|
||||
s.lastActivityMatches("", ``, lastActivity)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
func (s *integrationEnterpriseTestSuite) TestMDMAppleOSUpdates() {
|
||||
t := s.T()
|
||||
|
||||
// keep the last activity, to detect newly created ones
|
||||
@@ -2887,7 +3105,7 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
// get the appconfig, nothing changed
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.Equal(t, fleet.MacOSUpdates{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, acResp.MDM.MacOSUpdates)
|
||||
require.Equal(t, fleet.AppleOSUpdateSettings{MinimumVersion: optjson.String{Set: true}, Deadline: optjson.String{Set: true}}, acResp.MDM.MacOSUpdates)
|
||||
|
||||
// no activity got created
|
||||
activitiesResp = listActivitiesResponse{}
|
||||
@@ -2904,6 +3122,16 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ios_updates": {
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ipados_updates": {
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// missing deadline
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
@@ -2911,6 +3139,16 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"minimum_version": "12.1.1"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ios_updates": {
|
||||
"minimum_version": "12.1.1"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ipados_updates": {
|
||||
"minimum_version": "12.1.1"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// invalid deadline
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
@@ -2919,6 +3157,18 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"deadline": "2022"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ios_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ipados_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// deadline includes timestamp
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
@@ -2927,6 +3177,18 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"deadline": "2022-01-01T00:00:00Z"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ios_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022-01-01T00:00:00Z"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ipados_updates": {
|
||||
"minimum_version": "12.1.1",
|
||||
"deadline": "2022-01-01T00:00:00Z"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// minimum_version includes build info
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
@@ -2935,6 +3197,18 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ios_updates": {
|
||||
"minimum_version": "12.1.1 (ABCD)",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
checkInvalidConfig(`{"mdm": {
|
||||
"ipados_updates": {
|
||||
"minimum_version": "12.1.1 (ABCD)",
|
||||
"deadline": "2022-01-01"
|
||||
}
|
||||
}}`)
|
||||
|
||||
// valid config
|
||||
acResp := appConfigResponse{}
|
||||
@@ -2943,23 +3217,47 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2022-01-01"
|
||||
},
|
||||
"ios_updates": {
|
||||
"minimum_version": "13.13.13",
|
||||
"deadline": "2023-03-03"
|
||||
},
|
||||
"ipados_updates": {
|
||||
"minimum_version": "14.14.14",
|
||||
"deadline": "2024-04-04"
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2023-03-03", acResp.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-04-04", acResp.MDM.IPadOSUpdates.Deadline.Value)
|
||||
|
||||
// edited macos min version activity got created
|
||||
s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2022-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
|
||||
s.assertMacOSUpdatesDeclaration(nil, &fleet.MacOSUpdates{
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2022-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2023-03-03", "minimum_version":"13.13.13", "team_id": null, "team_name": null}`, 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2024-04-04", "minimum_version":"14.14.14", "team_id": null, "team_name": null}`, 0)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2022-01-01"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2023-03-03"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2024-04-04"),
|
||||
})
|
||||
|
||||
// get the appconfig
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/config", nil, http.StatusOK, &acResp)
|
||||
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2022-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2023-03-03", acResp.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-04-04", acResp.MDM.IPadOSUpdates.Deadline.Value)
|
||||
|
||||
// update the deadline
|
||||
acResp = appConfigResponse{}
|
||||
@@ -2968,17 +3266,37 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
"macos_updates": {
|
||||
"minimum_version": "12.3.1",
|
||||
"deadline": "2024-01-01"
|
||||
},
|
||||
"ios_updates": {
|
||||
"minimum_version": "13.13.13",
|
||||
"deadline": "2025-05-05"
|
||||
},
|
||||
"ipados_updates": {
|
||||
"minimum_version": "14.14.14",
|
||||
"deadline": "2026-06-06"
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Equal(t, "12.3.1", acResp.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2024-01-01", acResp.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "13.13.13", acResp.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2025-05-05", acResp.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Equal(t, "14.14.14", acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Equal(t, "2026-06-06", acResp.MDM.IPadOSUpdates.Deadline.Value)
|
||||
|
||||
// another edited macos min version activity got created
|
||||
lastActivity = s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2024-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
|
||||
s.assertMacOSUpdatesDeclaration(nil, &fleet.MacOSUpdates{
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"2024-01-01", "minimum_version":"12.3.1", "team_id": null, "team_name": null}`, 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"2025-05-05", "minimum_version":"13.13.13", "team_id": null, "team_name": null}`, 0)
|
||||
lastActivity = s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"2026-06-06", "minimum_version":"14.14.14", "team_id": null, "team_name": null}`, 0)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2024-01-01"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2025-05-05"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2026-06-06"),
|
||||
})
|
||||
|
||||
// update something unrelated - the transparency url
|
||||
acResp = appConfigResponse{}
|
||||
@@ -2988,43 +3306,81 @@ func (s *integrationEnterpriseTestSuite) TestMDMMacOSUpdates() {
|
||||
|
||||
// no activity got created
|
||||
s.lastActivityMatches("", ``, lastActivity)
|
||||
s.assertMacOSUpdatesDeclaration(nil, &fleet.MacOSUpdates{
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("12.3.1"), Deadline: optjson.SetString("2024-01-01"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("13.13.13"), Deadline: optjson.SetString("2025-05-05"),
|
||||
})
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, &fleet.AppleOSUpdateSettings{
|
||||
MinimumVersion: optjson.SetString("14.14.14"), Deadline: optjson.SetString("2026-06-06"),
|
||||
})
|
||||
|
||||
// clear the macos requirement
|
||||
// clear the apple OS requirements
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
},
|
||||
"ios_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
},
|
||||
"ipados_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Empty(t, acResp.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Empty(t, acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.IPadOSUpdates.Deadline.Value)
|
||||
|
||||
// edited macos min version activity got created with empty requirement
|
||||
lastActivity = s.lastActivityMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"", "minimum_version":"", "team_id": null, "team_name": null}`, 0)
|
||||
s.assertMacOSUpdatesDeclaration(nil, nil)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedMacOSMinVersion{}.ActivityName(), `{"deadline":"", "minimum_version":"", "team_id": null, "team_name": null}`, 0)
|
||||
s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIOSMinVersion{}.ActivityName(), `{"deadline":"", "minimum_version":"", "team_id": null, "team_name": null}`, 0)
|
||||
lastActivity = s.lastActivityOfTypeMatches(fleet.ActivityTypeEditedIPadOSMinVersion{}.ActivityName(), `{"deadline":"", "minimum_version":"", "team_id": null, "team_name": null}`, 0)
|
||||
|
||||
// update again with empty macos requirement
|
||||
// check DDM profiles were removed
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, nil)
|
||||
|
||||
// update again with empty apple OS requirements
|
||||
acResp = appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{
|
||||
"mdm": {
|
||||
"macos_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
},
|
||||
"ios_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
},
|
||||
"ipados_updates": {
|
||||
"minimum_version": "",
|
||||
"deadline": ""
|
||||
}
|
||||
}
|
||||
}`), http.StatusOK, &acResp)
|
||||
require.Empty(t, acResp.MDM.MacOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.MacOSUpdates.Deadline.Value)
|
||||
require.Empty(t, acResp.MDM.IOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.IOSUpdates.Deadline.Value)
|
||||
require.Empty(t, acResp.MDM.IPadOSUpdates.MinimumVersion.Value)
|
||||
require.Empty(t, acResp.MDM.IPadOSUpdates.Deadline.Value)
|
||||
|
||||
// no activity got created
|
||||
// no activity or DDM profiles were created
|
||||
s.lastActivityMatches("", ``, lastActivity)
|
||||
s.assertMacOSUpdatesDeclaration(nil, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetMacOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIOSUpdatesProfileName, nil)
|
||||
s.assertAppleOSUpdatesDeclaration(nil, mdm.FleetIPadOSUpdatesProfileName, nil)
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestSSOJITProvisioning() {
|
||||
@@ -11054,9 +11410,8 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareReconciliation() {
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
|
||||
t := s.T()
|
||||
t.Skip("disabled calendar callbacks to address bugs")
|
||||
ctx := context.Background()
|
||||
t := s.T()
|
||||
t.Cleanup(func() {
|
||||
calendar.ClearMockEvents()
|
||||
calendar.ClearMockChannels()
|
||||
@@ -11068,6 +11423,12 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
origRecentUpdateDuration := commonCalendar.RecentCalendarUpdateDuration
|
||||
commonCalendar.RecentCalendarUpdateDuration = 1 * time.Millisecond
|
||||
t.Cleanup(func() {
|
||||
commonCalendar.RecentCalendarUpdateDuration = origRecentUpdateDuration
|
||||
})
|
||||
|
||||
team1, err := s.ds.NewTeam(ctx, &fleet.Team{
|
||||
Name: "team1",
|
||||
})
|
||||
@@ -11312,8 +11673,9 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
team1CalendarEvents, err = s.ds.ListCalendarEvents(ctx, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, team1CalendarEvents, 1)
|
||||
if event.UUID != team1CalendarEvents[0].UUID {
|
||||
// Event should be rescheduled on a future date/time
|
||||
if len(team1CalendarEvents) == 1 && team1CalendarEvents[0].UUID == event.UUID &&
|
||||
team1CalendarEvents[0].StartTime.After(event.StartTime) {
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
@@ -11463,7 +11825,20 @@ func (s *integrationEnterpriseTestSuite) TestCalendarCallback() {
|
||||
},
|
||||
), http.StatusOK, &distributedResp)
|
||||
|
||||
// Callback should still work, but only clear the callback channel. Event in DB will be deleted on the next cron run.
|
||||
// We set a flag that event was updated recently. Callback shouldn't do anything since event was updated recently
|
||||
_, err = distributedLock.AcquireLock(ctx, commonCalendar.RecentUpdateKeyPrefix+event.UUID, commonCalendar.RecentCalendarUpdateValue,
|
||||
1000)
|
||||
require.NoError(t, err)
|
||||
_ = s.DoRawWithHeaders("POST", "/api/v1/fleet/calendar/webhook/"+eventRecreated.UUID, []byte(""), http.StatusOK,
|
||||
map[string]string{
|
||||
"X-Goog-Channel-Id": details.ChannelID,
|
||||
"X-Goog-Resource-State": "exists",
|
||||
})
|
||||
assert.Equal(t, 1, calendar.MockChannelsCount())
|
||||
|
||||
// Callback should work, but only clear the callback channel. Event in DB will be deleted on the next cron run.
|
||||
_, err = distributedLock.ReleaseLock(ctx, commonCalendar.RecentUpdateKeyPrefix+event.UUID, commonCalendar.RecentCalendarUpdateValue)
|
||||
require.NoError(t, err)
|
||||
_ = s.DoRawWithHeaders("POST", "/api/v1/fleet/calendar/webhook/"+eventRecreated.UUID, []byte(""), http.StatusOK,
|
||||
map[string]string{
|
||||
"X-Goog-Channel-Id": details.ChannelID,
|
||||
|
||||
@@ -364,7 +364,7 @@ func (s *integrationMDMTestSuite) TestAppleProfileManagement() {
|
||||
}`), http.StatusOK)
|
||||
s.Do("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", tm.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamPayloadMDM{
|
||||
MacOSUpdates: &fleet.MacOSUpdates{
|
||||
MacOSUpdates: &fleet.AppleOSUpdateSettings{
|
||||
Deadline: optjson.SetString("1992-01-01"),
|
||||
MinimumVersion: optjson.SetString("13.1.1"),
|
||||
},
|
||||
@@ -2865,28 +2865,33 @@ func (s *integrationMDMTestSuite) TestMDMConfigProfileCRUD() {
|
||||
{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},
|
||||
{ProfileUUID: uuidAppleDDMWithLabel, Platform: "darwin", Name: "apple-decl-with-labels", Identifier: "ident-decl-with-labels", TeamID: nil,
|
||||
{
|
||||
ProfileUUID: uuidAppleDDMWithLabel, Platform: "darwin", Name: "apple-decl-with-labels", Identifier: "ident-decl-with-labels", TeamID: nil,
|
||||
LabelsIncludeAll: []fleet.ConfigurationProfileLabel{
|
||||
{LabelID: labelFoo.ID, LabelName: labelFoo.Name},
|
||||
},
|
||||
},
|
||||
{ProfileUUID: uuidAppleWithLabel, Platform: "darwin", Name: "apple-profile-with-labels", Identifier: "ident-with-labels", TeamID: nil,
|
||||
{
|
||||
ProfileUUID: uuidAppleWithLabel, Platform: "darwin", Name: "apple-profile-with-labels", Identifier: "ident-with-labels", TeamID: nil,
|
||||
LabelsIncludeAll: []fleet.ConfigurationProfileLabel{
|
||||
{LabelID: labelFoo.ID, LabelName: labelFoo.Name},
|
||||
},
|
||||
},
|
||||
{ProfileUUID: uuidWindowsWithLabel, Platform: "windows", Name: "win-profile-with-labels", TeamID: nil,
|
||||
{
|
||||
ProfileUUID: uuidWindowsWithLabel, Platform: "windows", Name: "win-profile-with-labels", TeamID: nil,
|
||||
LabelsExcludeAny: []fleet.ConfigurationProfileLabel{
|
||||
{LabelID: labelBar.ID, LabelName: labelBar.Name},
|
||||
{LabelID: labelFoo.ID, LabelName: labelFoo.Name},
|
||||
},
|
||||
},
|
||||
{ProfileUUID: uuidAppleDDMTeamWithLabel, Platform: "darwin", Name: "apple-team-decl-with-labels", Identifier: "ident-team-decl-with-labels", TeamID: &testTeam.ID,
|
||||
{
|
||||
ProfileUUID: uuidAppleDDMTeamWithLabel, Platform: "darwin", Name: "apple-team-decl-with-labels", Identifier: "ident-team-decl-with-labels", TeamID: &testTeam.ID,
|
||||
LabelsExcludeAny: []fleet.ConfigurationProfileLabel{
|
||||
{LabelID: labelFoo.ID, LabelName: labelFoo.Name},
|
||||
},
|
||||
},
|
||||
{ProfileUUID: uuidWindowsTeamWithLabel, Platform: "windows", Name: "win-team-profile-with-labels", TeamID: &testTeam.ID,
|
||||
{
|
||||
ProfileUUID: uuidWindowsTeamWithLabel, Platform: "windows", Name: "win-team-profile-with-labels", TeamID: &testTeam.ID,
|
||||
LabelsIncludeAll: []fleet.ConfigurationProfileLabel{
|
||||
{LabelID: labelBar.ID, LabelName: labelBar.Name},
|
||||
{LabelID: labelFoo.ID, LabelName: labelFoo.Name},
|
||||
@@ -4282,7 +4287,7 @@ func (s *integrationMDMTestSuite) TestMDMBatchSetProfilesKeepsReservedNames() {
|
||||
DeadlineDays: optjson.SetInt(4),
|
||||
GracePeriodDays: optjson.SetInt(1),
|
||||
},
|
||||
MacOSUpdates: &fleet.MacOSUpdates{
|
||||
MacOSUpdates: &fleet.AppleOSUpdateSettings{
|
||||
Deadline: optjson.SetString("2023-12-31"),
|
||||
MinimumVersion: optjson.SetString("13.3.8"),
|
||||
},
|
||||
|
||||
@@ -959,6 +959,43 @@ func createHostThenEnrollMDM(ds fleet.Datastore, fleetServerURL string, t *testi
|
||||
return fleetHost, mdmDevice
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) createAppleMobileHostThenEnrollMDM(platform string) (*fleet.Host, *mdmtest.TestAppleMDMClient) {
|
||||
ctx := context.Background()
|
||||
t := s.T()
|
||||
|
||||
// create a host with minimal information and the serial, no uuid/osquery id
|
||||
// (as when created via DEP sync).
|
||||
dbZeroTime := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
serialNumber := mdmtest.RandSerialNumber()
|
||||
fleetHost, err := s.ds.NewHost(ctx, &fleet.Host{
|
||||
HardwareSerial: serialNumber,
|
||||
Platform: platform,
|
||||
LastEnrolledAt: dbZeroTime,
|
||||
DetailUpdatedAt: dbZeroTime,
|
||||
RefetchRequested: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dbZeroTime, fleetHost.LastEnrolledAt)
|
||||
|
||||
// Perform the MDM enrollment.
|
||||
mdmEnrollInfo := mdmtest.AppleEnrollInfo{
|
||||
SCEPChallenge: s.scepChallenge,
|
||||
SCEPURL: s.server.URL + apple_mdm.SCEPPath,
|
||||
MDMURL: s.server.URL + apple_mdm.MDMPath,
|
||||
}
|
||||
model := "iPhone14,6"
|
||||
if platform == "ipados" {
|
||||
model = "iPad13,18"
|
||||
}
|
||||
mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, model)
|
||||
mdmDevice.SerialNumber = serialNumber
|
||||
err = mdmDevice.Enroll()
|
||||
require.NoError(t, err)
|
||||
|
||||
return fleetHost, mdmDevice
|
||||
|
||||
}
|
||||
|
||||
func createWindowsHostThenEnrollMDM(ds fleet.Datastore, fleetServerURL string, t *testing.T) (*fleet.Host, *mdmtest.TestWindowsMDMClient) {
|
||||
host := createOrbitEnrolledHost(t, "windows", "h1", ds)
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsProgramatic(fleetServerURL, *host.OrbitNodeKey)
|
||||
@@ -5610,7 +5647,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
|
||||
|
||||
resp = orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
wantCfg, err := fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
|
||||
wantCfg, err := fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantCfg, resp.NudgeConfig)
|
||||
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 04:00:00 +0000 UTC")
|
||||
@@ -5638,7 +5675,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
|
||||
var tmResp teamResponse
|
||||
s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d", team.ID), fleet.TeamPayload{
|
||||
MDM: &fleet.TeamPayloadMDM{
|
||||
MacOSUpdates: &fleet.MacOSUpdates{
|
||||
MacOSUpdates: &fleet.AppleOSUpdateSettings{
|
||||
Deadline: optjson.SetString("1992-01-01"),
|
||||
MinimumVersion: optjson.SetString("13.1.1"),
|
||||
},
|
||||
@@ -5648,7 +5685,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
|
||||
|
||||
resp = orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("1992-01-01"), MinimumVersion: optjson.SetString("13.1.1")})
|
||||
wantCfg, err = fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("1992-01-01"), MinimumVersion: optjson.SetString("13.1.1")})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantCfg, resp.NudgeConfig)
|
||||
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "1992-01-01 04:00:00 +0000 UTC")
|
||||
@@ -5670,7 +5707,7 @@ func (s *integrationMDMTestSuite) TestOrbitConfigNudgeSettings() {
|
||||
|
||||
resp = orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h2.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
wantCfg, err = fleet.NewNudgeConfig(fleet.MacOSUpdates{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
|
||||
wantCfg, err = fleet.NewNudgeConfig(fleet.AppleOSUpdateSettings{Deadline: optjson.SetString("2022-01-04"), MinimumVersion: optjson.SetString("12.1.3")})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantCfg, resp.NudgeConfig)
|
||||
require.Equal(t, wantCfg.OSVersionRequirements[0].RequiredInstallationDate.String(), "2022-01-04 04:00:00 +0000 UTC")
|
||||
@@ -9619,39 +9656,58 @@ func (s *integrationMDMTestSuite) TestInvalidCommandUUID() {
|
||||
|
||||
func (s *integrationMDMTestSuite) TestEnrollAfterDEPSyncIOSIPadOS() {
|
||||
t := s.T()
|
||||
ctx := context.Background()
|
||||
|
||||
// create a host with minimal information and the serial, no uuid/osquery id
|
||||
// (as when created via DEP sync).
|
||||
dbZeroTime := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
serialNumber := mdmtest.RandSerialNumber()
|
||||
h, err := s.ds.NewHost(ctx, &fleet.Host{
|
||||
HardwareSerial: serialNumber,
|
||||
Platform: "ios",
|
||||
LastEnrolledAt: dbZeroTime,
|
||||
DetailUpdatedAt: dbZeroTime,
|
||||
RefetchRequested: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dbZeroTime, h.LastEnrolledAt)
|
||||
|
||||
// Perform the MDM enrollment.
|
||||
mdmEnrollInfo := mdmtest.AppleEnrollInfo{
|
||||
SCEPChallenge: s.scepChallenge,
|
||||
SCEPURL: s.server.URL + apple_mdm.SCEPPath,
|
||||
MDMURL: s.server.URL + apple_mdm.MDMPath,
|
||||
}
|
||||
mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmEnrollInfo, "iPhone14,6")
|
||||
mdmDevice.SerialNumber = serialNumber
|
||||
err = mdmDevice.Enroll()
|
||||
require.NoError(t, err)
|
||||
h, _ := s.createAppleMobileHostThenEnrollMDM("ios")
|
||||
|
||||
// fetch the host, it will match the one created above
|
||||
// (NOTE: cannot check the returned OrbitNodeKey, this field is not part of the response)
|
||||
var hostResp getHostResponse
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResp)
|
||||
require.Equal(t, h.ID, hostResp.Host.ID)
|
||||
require.NotEqual(t, dbZeroTime, hostResp.Host.LastEnrolledAt)
|
||||
require.NotEqual(t, h.LastEnrolledAt, hostResp.Host.LastEnrolledAt)
|
||||
|
||||
h, _ = s.createAppleMobileHostThenEnrollMDM("ipados")
|
||||
|
||||
// fetch the host, it will match the one created above
|
||||
// (NOTE: cannot check the returned OrbitNodeKey, this field is not part of the response)
|
||||
hostResp = getHostResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h.ID), nil, http.StatusOK, &hostResp)
|
||||
require.Equal(t, h.ID, hostResp.Host.ID)
|
||||
require.NotEqual(t, h.LastEnrolledAt, hostResp.Host.LastEnrolledAt)
|
||||
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestRefetchIOSIPadOS() {
|
||||
t := s.T()
|
||||
|
||||
// Try to refetch host that is not MDM enrolled
|
||||
serialNumber := mdmtest.RandSerialNumber()
|
||||
fleetHost, err := s.ds.NewHost(context.Background(), &fleet.Host{
|
||||
HardwareSerial: serialNumber,
|
||||
Platform: "ipados",
|
||||
LastEnrolledAt: time.Now(),
|
||||
DetailUpdatedAt: time.Now(),
|
||||
RefetchRequested: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
r := s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/refetch", fleetHost.ID), nil, http.StatusUnprocessableEntity, "error",
|
||||
"host is not enrolled in MDM")
|
||||
assert.Contains(t, extractServerErrorText(r.Body), "Host does not have MDM turned on")
|
||||
|
||||
// Try to refetch an MDM enrolled host
|
||||
host, mdmClient := s.createAppleMobileHostThenEnrollMDM("ios")
|
||||
_ = s.Do("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/refetch", host.ID), nil, http.StatusOK)
|
||||
|
||||
// Check the MDM command
|
||||
cmd, err := mdmClient.Idle()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cmd)
|
||||
assert.Equal(t, "DeviceInformation", cmd.Command.RequestType)
|
||||
|
||||
var hostResp getHostResponse
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &hostResp)
|
||||
assert.Equal(t, host.ID, hostResp.Host.ID)
|
||||
assert.True(t, host.RefetchRequested)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestVPPApps() {
|
||||
|
||||
@@ -188,7 +188,7 @@ func newTestServiceWithConfig(t *testing.T, ds fleet.Datastore, fleetConfig conf
|
||||
svc, err = eeservice.NewService(
|
||||
svc,
|
||||
ds,
|
||||
kitlog.NewNopLogger(),
|
||||
logger,
|
||||
fleetConfig,
|
||||
mailer,
|
||||
c,
|
||||
|
||||
@@ -318,7 +318,9 @@ func TestTranslateCPEToCVE(t *testing.T) {
|
||||
{ID: "CVE-2023-42950", resolvedInVersion: "17.2"},
|
||||
{ID: "CVE-2024-23273", resolvedInVersion: "17.4"},
|
||||
},
|
||||
excludedCVEs: []string{"CVE-2023-28205"},
|
||||
excludedCVEs: []string{
|
||||
"CVE-2023-28205", // This vulnerability is for Safari 16.4.0
|
||||
},
|
||||
continuesToUpdate: true,
|
||||
},
|
||||
"cpe:2.3:a:apple:safari:16.4.0:*:*:*:*:macos:*:*": {
|
||||
|
||||
@@ -199,6 +199,9 @@ func (s *CVE) updateYearFile(year int, cves []nvdapi.CVEItem) error {
|
||||
// Convert new API 2.0 format to legacy feed format and create map of new CVE information.
|
||||
newLegacyCVEs := make(map[string]*schema.NVDCVEFeedJSON10DefCVEItem)
|
||||
for _, cve := range cves {
|
||||
if cve.CVE.VulnStatus != nil && *cve.CVE.VulnStatus == "Rejected" {
|
||||
continue
|
||||
}
|
||||
legacyCVE := convertAPI20CVEToLegacy(cve.CVE, s.logger)
|
||||
newLegacyCVEs[legacyCVE.CVE.CVEDataMeta.ID] = legacyCVE
|
||||
}
|
||||
@@ -249,6 +252,9 @@ func (s *CVE) updateVulnCheckYearFile(year int, cves []VulnCheckCVE, modCount, a
|
||||
// Convert new API 2.0 format to legacy feed format and create map of new CVE information.
|
||||
newLegacyCVEs := make(map[string]*schema.NVDCVEFeedJSON10DefCVEItem)
|
||||
for _, cve := range cves {
|
||||
if cve.CVE.VulnStatus != nil && *cve.CVE.VulnStatus == "Rejected" {
|
||||
continue
|
||||
}
|
||||
legacyCVE := convertAPI20CVEToLegacy(cve.CVE, s.logger)
|
||||
updateWithVulnCheckConfigurations(legacyCVE, cve.VcConfigurations)
|
||||
newLegacyCVEs[legacyCVE.CVE.CVEDataMeta.ID] = legacyCVE
|
||||
|
||||
Reference in New Issue
Block a user