diff --git a/changes/18849-config-profiles-exclude-labels b/changes/18849-config-profiles-exclude-labels
new file mode 100644
index 0000000000..4c1d41ecfb
--- /dev/null
+++ b/changes/18849-config-profiles-exclude-labels
@@ -0,0 +1,5 @@
+* Added the database migrations to create the new `exclude` column for labels associated with MDM profiles (and declarations).
+* Added the API changes to support the `labels_include_all` and `labels_exclude_any` fields (and accept the deprecated `labels` field as an alias for `labels_include_all`).
+* Added `fleetctl gitops` and `fleetctl apply` support for `labels_include_all` and `labels_exclude_any` to configure a custom setting.
+* Updated the profile reconciliation logic to handle the new "exclude any" labels.
+* Fix bug where macOS declarations were stuck in "to be removed" state indefinitely.
diff --git a/changes/issue-18848-include-all-exclude-any-custom-profile b/changes/issue-18848-include-all-exclude-any-custom-profile
new file mode 100644
index 0000000000..ef065303f3
--- /dev/null
+++ b/changes/issue-18848-include-all-exclude-any-custom-profile
@@ -0,0 +1,2 @@
+- add UI for uploading custom profiles with a target of hosts that include all/exclude
+ any selected labels
diff --git a/cmd/fleetctl/gitops_test.go b/cmd/fleetctl/gitops_test.go
index e8569360ea..d59af1bd5a 100644
--- a/cmd/fleetctl/gitops_test.go
+++ b/cmd/fleetctl/gitops_test.go
@@ -1088,6 +1088,53 @@ func TestTeamSofwareInstallersGitOps(t *testing.T) {
}
}
+func TestCustomSettingsGitOps(t *testing.T) {
+ cases := []struct {
+ file string
+ wantErr string
+ }{
+ {"testdata/gitops/global_macos_windows_custom_settings_valid.yml", ""},
+ {"testdata/gitops/global_macos_custom_settings_valid_deprecated.yml", ""},
+ {"testdata/gitops/global_windows_custom_settings_invalid_label_mix.yml", `For each profile, only one of "labels_exclude_any", "labels_include_all" or "labels" can be included`},
+ {"testdata/gitops/global_windows_custom_settings_unknown_label.yml", `some or all the labels provided don't exist`},
+ {"testdata/gitops/team_macos_windows_custom_settings_valid.yml", ""},
+ {"testdata/gitops/team_macos_custom_settings_valid_deprecated.yml", ""},
+ {"testdata/gitops/team_macos_windows_custom_settings_invalid_labels_mix.yml", `For each profile, only one of "labels_exclude_any", "labels_include_all" or "labels" can be included.`},
+ {"testdata/gitops/team_macos_windows_custom_settings_unknown_label.yml", `some or all the labels provided don't exist`},
+ }
+ for _, c := range cases {
+ t.Run(filepath.Base(c.file), func(t *testing.T) {
+ ds, appCfgPtr, _ := setupFullGitOpsPremiumServer(t)
+ (*appCfgPtr).MDM.EnabledAndConfigured = true
+ (*appCfgPtr).MDM.WindowsEnabledAndConfigured = true
+ labelToIDs := map[string]uint{
+ fleet.BuiltinLabelMacOS14Plus: 1,
+ "A": 2,
+ "B": 3,
+ "C": 4,
+ }
+ ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
+ // for this test, recognize labels A, B and C (as well as the built-in macos 14+ one)
+ ret := make(map[string]uint)
+ for _, lbl := range labels {
+ id, ok := labelToIDs[lbl]
+ if ok {
+ ret[lbl] = id
+ }
+ }
+ return ret, nil
+ }
+
+ _, err := runAppNoChecks([]string{"gitops", "-f", c.file})
+ if c.wantErr == "" {
+ require.NoError(t, err)
+ } else {
+ require.ErrorContains(t, err, c.wantErr)
+ }
+ })
+ }
+}
+
func startSoftwareInstallerServer(t *testing.T) {
// start the web server that will serve the installer
b, err := os.ReadFile(filepath.Join("..", "..", "server", "service", "testdata", "software-installers", "ruby.deb"))
diff --git a/cmd/fleetctl/testdata/gitops/global_macos_custom_settings_valid_deprecated.yml b/cmd/fleetctl/testdata/gitops/global_macos_custom_settings_valid_deprecated.yml
new file mode 100644
index 0000000000..177c1c80cf
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/global_macos_custom_settings_valid_deprecated.yml
@@ -0,0 +1,93 @@
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels:
+ - A
+ scripts:
+ enable_disk_encryption: false
+ macos_migration:
+ enable: false
+ mode: ""
+ webhook_url: ""
+ macos_setup:
+ bootstrap_package: null
+ enable_end_user_authentication: false
+ macos_setup_assistant: null
+ macos_updates:
+ deadline: null
+ minimum_version: null
+ windows_enabled_and_configured: true
+ windows_updates:
+ deadline_days: null
+ grace_period_days: null
+queries:
+policies:
+agent_options:
+ command_line_flags:
+ distributed_denylist_duration: 0
+ config:
+ options:
+ disable_distributed: false
+ distributed_interval: 10
+ distributed_plugin: tls
+ distributed_tls_max_attempts: 3
+ logger_tls_endpoint: /api/v1/osquery/log
+ pack_delimiter: /
+org_settings:
+ server_settings:
+ deferred_save_host: false
+ enable_analytics: true
+ live_query_disabled: false
+ query_report_cap: 2000
+ query_reports_disabled: false
+ scripts_disabled: false
+ server_url: $FLEET_SERVER_URL
+ ai_features_disabled: true
+ org_info:
+ contact_url: https://fleetdm.com/company/contact
+ org_logo_url: ""
+ org_logo_url_light_background: ""
+ org_name: $ORG_NAME
+ smtp_settings:
+ authentication_method: authmethod_plain
+ authentication_type: authtype_username_password
+ configured: false
+ domain: ""
+ enable_smtp: false
+ enable_ssl_tls: true
+ enable_start_tls: true
+ password: ""
+ port: 587
+ sender_address: ""
+ server: ""
+ user_name: ""
+ verify_ssl_certs: true
+ sso_settings:
+ enable_jit_provisioning: false
+ enable_jit_role_sync: false
+ enable_sso: true
+ enable_sso_idp_login: false
+ entity_id: https://saml.example.com/entityid
+ idp_image_url: ""
+ idp_name: MockSAML
+ issuer_uri: ""
+ metadata: ""
+ metadata_url: https://mocksaml.com/api/saml/metadata
+ integrations:
+ mdm:
+ webhook_settings:
+ fleet_desktop:
+ transparency_url: https://fleetdm.com/transparency
+ host_expiry_settings:
+ host_expiry_enabled: false
+ activity_expiry_settings:
+ activity_expiry_enabled: true
+ activity_expiry_window: 60
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ vulnerability_settings:
+ databases_path: ""
+ secrets:
+ - secret: ABC
diff --git a/cmd/fleetctl/testdata/gitops/global_macos_windows_custom_settings_valid.yml b/cmd/fleetctl/testdata/gitops/global_macos_windows_custom_settings_valid.yml
new file mode 100644
index 0000000000..da75847cd5
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/global_macos_windows_custom_settings_valid.yml
@@ -0,0 +1,99 @@
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels_include_all:
+ - A
+ - B
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_exclude_any:
+ - C
+ scripts:
+ enable_disk_encryption: false
+ macos_migration:
+ enable: false
+ mode: ""
+ webhook_url: ""
+ macos_setup:
+ bootstrap_package: null
+ enable_end_user_authentication: false
+ macos_setup_assistant: null
+ macos_updates:
+ deadline: null
+ minimum_version: null
+ windows_enabled_and_configured: true
+ windows_updates:
+ deadline_days: null
+ grace_period_days: null
+queries:
+policies:
+agent_options:
+ command_line_flags:
+ distributed_denylist_duration: 0
+ config:
+ options:
+ disable_distributed: false
+ distributed_interval: 10
+ distributed_plugin: tls
+ distributed_tls_max_attempts: 3
+ logger_tls_endpoint: /api/v1/osquery/log
+ pack_delimiter: /
+org_settings:
+ server_settings:
+ deferred_save_host: false
+ enable_analytics: true
+ live_query_disabled: false
+ query_report_cap: 2000
+ query_reports_disabled: false
+ scripts_disabled: false
+ server_url: $FLEET_SERVER_URL
+ ai_features_disabled: true
+ org_info:
+ contact_url: https://fleetdm.com/company/contact
+ org_logo_url: ""
+ org_logo_url_light_background: ""
+ org_name: $ORG_NAME
+ smtp_settings:
+ authentication_method: authmethod_plain
+ authentication_type: authtype_username_password
+ configured: false
+ domain: ""
+ enable_smtp: false
+ enable_ssl_tls: true
+ enable_start_tls: true
+ password: ""
+ port: 587
+ sender_address: ""
+ server: ""
+ user_name: ""
+ verify_ssl_certs: true
+ sso_settings:
+ enable_jit_provisioning: false
+ enable_jit_role_sync: false
+ enable_sso: true
+ enable_sso_idp_login: false
+ entity_id: https://saml.example.com/entityid
+ idp_image_url: ""
+ idp_name: MockSAML
+ issuer_uri: ""
+ metadata: ""
+ metadata_url: https://mocksaml.com/api/saml/metadata
+ integrations:
+ mdm:
+ webhook_settings:
+ fleet_desktop:
+ transparency_url: https://fleetdm.com/transparency
+ host_expiry_settings:
+ host_expiry_enabled: false
+ activity_expiry_settings:
+ activity_expiry_enabled: true
+ activity_expiry_window: 60
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ vulnerability_settings:
+ databases_path: ""
+ secrets:
+ - secret: ABC
diff --git a/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_invalid_label_mix.yml b/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_invalid_label_mix.yml
new file mode 100644
index 0000000000..9d2ac6e69f
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_invalid_label_mix.yml
@@ -0,0 +1,95 @@
+controls:
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_include_all:
+ - B
+ labels_exclude_any:
+ - C
+ scripts:
+ enable_disk_encryption: false
+ macos_migration:
+ enable: false
+ mode: ""
+ webhook_url: ""
+ macos_setup:
+ bootstrap_package: null
+ enable_end_user_authentication: false
+ macos_setup_assistant: null
+ macos_updates:
+ deadline: null
+ minimum_version: null
+ windows_enabled_and_configured: true
+ windows_updates:
+ deadline_days: null
+ grace_period_days: null
+queries:
+policies:
+agent_options:
+ command_line_flags:
+ distributed_denylist_duration: 0
+ config:
+ options:
+ disable_distributed: false
+ distributed_interval: 10
+ distributed_plugin: tls
+ distributed_tls_max_attempts: 3
+ logger_tls_endpoint: /api/v1/osquery/log
+ pack_delimiter: /
+org_settings:
+ server_settings:
+ deferred_save_host: false
+ enable_analytics: true
+ live_query_disabled: false
+ query_report_cap: 2000
+ query_reports_disabled: false
+ scripts_disabled: false
+ server_url: $FLEET_SERVER_URL
+ ai_features_disabled: true
+ org_info:
+ contact_url: https://fleetdm.com/company/contact
+ org_logo_url: ""
+ org_logo_url_light_background: ""
+ org_name: $ORG_NAME
+ smtp_settings:
+ authentication_method: authmethod_plain
+ authentication_type: authtype_username_password
+ configured: false
+ domain: ""
+ enable_smtp: false
+ enable_ssl_tls: true
+ enable_start_tls: true
+ password: ""
+ port: 587
+ sender_address: ""
+ server: ""
+ user_name: ""
+ verify_ssl_certs: true
+ sso_settings:
+ enable_jit_provisioning: false
+ enable_jit_role_sync: false
+ enable_sso: true
+ enable_sso_idp_login: false
+ entity_id: https://saml.example.com/entityid
+ idp_image_url: ""
+ idp_name: MockSAML
+ issuer_uri: ""
+ metadata: ""
+ metadata_url: https://mocksaml.com/api/saml/metadata
+ integrations:
+ mdm:
+ webhook_settings:
+ fleet_desktop:
+ transparency_url: https://fleetdm.com/transparency
+ host_expiry_settings:
+ host_expiry_enabled: false
+ activity_expiry_settings:
+ activity_expiry_enabled: true
+ activity_expiry_window: 60
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ vulnerability_settings:
+ databases_path: ""
+ secrets:
+ - secret: ABC
diff --git a/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_unknown_label.yml b/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_unknown_label.yml
new file mode 100644
index 0000000000..ba1d06f784
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/global_windows_custom_settings_unknown_label.yml
@@ -0,0 +1,93 @@
+controls:
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_include_all:
+ - ZZZ
+ scripts:
+ enable_disk_encryption: false
+ macos_migration:
+ enable: false
+ mode: ""
+ webhook_url: ""
+ macos_setup:
+ bootstrap_package: null
+ enable_end_user_authentication: false
+ macos_setup_assistant: null
+ macos_updates:
+ deadline: null
+ minimum_version: null
+ windows_enabled_and_configured: true
+ windows_updates:
+ deadline_days: null
+ grace_period_days: null
+queries:
+policies:
+agent_options:
+ command_line_flags:
+ distributed_denylist_duration: 0
+ config:
+ options:
+ disable_distributed: false
+ distributed_interval: 10
+ distributed_plugin: tls
+ distributed_tls_max_attempts: 3
+ logger_tls_endpoint: /api/v1/osquery/log
+ pack_delimiter: /
+org_settings:
+ server_settings:
+ deferred_save_host: false
+ enable_analytics: true
+ live_query_disabled: false
+ query_report_cap: 2000
+ query_reports_disabled: false
+ scripts_disabled: false
+ server_url: $FLEET_SERVER_URL
+ ai_features_disabled: true
+ org_info:
+ contact_url: https://fleetdm.com/company/contact
+ org_logo_url: ""
+ org_logo_url_light_background: ""
+ org_name: $ORG_NAME
+ smtp_settings:
+ authentication_method: authmethod_plain
+ authentication_type: authtype_username_password
+ configured: false
+ domain: ""
+ enable_smtp: false
+ enable_ssl_tls: true
+ enable_start_tls: true
+ password: ""
+ port: 587
+ sender_address: ""
+ server: ""
+ user_name: ""
+ verify_ssl_certs: true
+ sso_settings:
+ enable_jit_provisioning: false
+ enable_jit_role_sync: false
+ enable_sso: true
+ enable_sso_idp_login: false
+ entity_id: https://saml.example.com/entityid
+ idp_image_url: ""
+ idp_name: MockSAML
+ issuer_uri: ""
+ metadata: ""
+ metadata_url: https://mocksaml.com/api/saml/metadata
+ integrations:
+ mdm:
+ webhook_settings:
+ fleet_desktop:
+ transparency_url: https://fleetdm.com/transparency
+ host_expiry_settings:
+ host_expiry_enabled: false
+ activity_expiry_settings:
+ activity_expiry_enabled: true
+ activity_expiry_window: 60
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ vulnerability_settings:
+ databases_path: ""
+ secrets:
+ - secret: ABC
diff --git a/cmd/fleetctl/testdata/gitops/team_macos_custom_settings_valid_deprecated.yml b/cmd/fleetctl/testdata/gitops/team_macos_custom_settings_valid_deprecated.yml
new file mode 100644
index 0000000000..f6b6bf1051
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/team_macos_custom_settings_valid_deprecated.yml
@@ -0,0 +1,21 @@
+name: "${TEST_TEAM_NAME}"
+team_settings:
+ secrets:
+ - secret: "ABC"
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ host_expiry_settings:
+ host_expiry_enabled: true
+ host_expiry_window: 30
+agent_options:
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels:
+ - A
+ - B
+policies:
+queries:
+software:
diff --git a/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_invalid_labels_mix.yml b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_invalid_labels_mix.yml
new file mode 100644
index 0000000000..8e136a2bed
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_invalid_labels_mix.yml
@@ -0,0 +1,29 @@
+name: "${TEST_TEAM_NAME}"
+team_settings:
+ secrets:
+ - secret: "ABC"
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ host_expiry_settings:
+ host_expiry_enabled: true
+ host_expiry_window: 30
+agent_options:
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels_include_all:
+ - A
+ labels:
+ - B
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_include_all:
+ - A
+ labels_exclude_any:
+ - C
+policies:
+queries:
+software:
diff --git a/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_unknown_label.yml b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_unknown_label.yml
new file mode 100644
index 0000000000..37aa4a8aa9
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_unknown_label.yml
@@ -0,0 +1,25 @@
+name: "${TEST_TEAM_NAME}"
+team_settings:
+ secrets:
+ - secret: "ABC"
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ host_expiry_settings:
+ host_expiry_enabled: true
+ host_expiry_window: 30
+agent_options:
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels_include_all:
+ - ZZZ
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_exclude_any:
+ - ZZZ
+policies:
+queries:
+software:
diff --git a/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_valid.yml b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_valid.yml
new file mode 100644
index 0000000000..9f46ab2576
--- /dev/null
+++ b/cmd/fleetctl/testdata/gitops/team_macos_windows_custom_settings_valid.yml
@@ -0,0 +1,26 @@
+name: "${TEST_TEAM_NAME}"
+team_settings:
+ secrets:
+ - secret: "ABC"
+ features:
+ enable_host_users: true
+ enable_software_inventory: true
+ host_expiry_settings:
+ host_expiry_enabled: true
+ host_expiry_window: 30
+agent_options:
+controls:
+ macos_settings:
+ custom_settings:
+ - path: ./lib/macos-password.mobileconfig
+ labels_include_all:
+ - A
+ - B
+ windows_settings:
+ custom_settings:
+ - path: ./lib/windows-screenlock.xml
+ labels_exclude_any:
+ - C
+policies:
+queries:
+software:
diff --git a/ee/server/service/mdm.go b/ee/server/service/mdm.go
index cfaae9d50d..d433f5d246 100644
--- a/ee/server/service/mdm.go
+++ b/ee/server/service/mdm.go
@@ -1119,7 +1119,7 @@ func (svc *Service) mdmAppleEditedMacOSUpdates(ctx context.Context, teamID *uint
if err != nil {
return err
}
- d.Labels = []fleet.ConfigurationProfileLabel{
+ d.LabelsIncludeAll = []fleet.ConfigurationProfileLabel{
{LabelName: fleet.BuiltinLabelMacOS14Plus, LabelID: lblIDs[fleet.BuiltinLabelMacOS14Plus]},
}
diff --git a/ee/server/service/teams.go b/ee/server/service/teams.go
index 85e628f1af..ebdb716a07 100644
--- a/ee/server/service/teams.go
+++ b/ee/server/service/teams.go
@@ -950,6 +950,9 @@ func (svc *Service) createTeamFromSpec(
)
}
+ validateTeamCustomSettings(invalid, "macos", macOSSettings.CustomSettings)
+ validateTeamCustomSettings(invalid, "windows", spec.MDM.WindowsSettings.CustomSettings.Value)
+
var hostExpirySettings fleet.HostExpirySettings
if spec.HostExpirySettings != nil {
if spec.HostExpirySettings.HostExpiryEnabled && spec.HostExpirySettings.HostExpiryWindow <= 0 {
@@ -1006,6 +1009,7 @@ func (svc *Service) createTeamFromSpec(
WindowsUpdates: spec.MDM.WindowsUpdates,
MacOSSettings: macOSSettings,
MacOSSetup: macOSSetup,
+ WindowsSettings: spec.MDM.WindowsSettings,
},
HostExpirySettings: hostExpirySettings,
WebhookSettings: fleet.TeamWebhookSettings{
@@ -1183,6 +1187,9 @@ func (svc *Service) editTeamFromSpec(
team.Config.HostExpirySettings = *spec.HostExpirySettings
}
+ validateTeamCustomSettings(invalid, "macos", team.Config.MDM.MacOSSettings.CustomSettings)
+ validateTeamCustomSettings(invalid, "windows", team.Config.MDM.WindowsSettings.CustomSettings.Value)
+
// If host status webhook is not provided, do not change it
if spec.WebhookSettings.HostStatusWebhook != nil {
fleet.ValidateEnabledHostStatusIntegrations(*spec.WebhookSettings.HostStatusWebhook, invalid)
@@ -1282,6 +1289,25 @@ func (svc *Service) editTeamFromSpec(
return nil
}
+func validateTeamCustomSettings(invalid *fleet.InvalidArgumentError, prefix string, customSettings []fleet.MDMProfileSpec) {
+ for i, prof := range customSettings {
+ count := 0
+ for _, b := range []bool{len(prof.Labels) > 0, len(prof.LabelsIncludeAll) > 0, len(prof.LabelsExcludeAny) > 0} {
+ if b {
+ count++
+ }
+ }
+ if count > 1 {
+ invalid.Append(fmt.Sprintf("%s_settings.custom_settings", prefix),
+ fmt.Sprintf(`Couldn't edit %s_settings.custom_settings. For each profile, only one of "labels_exclude_any", "labels_include_all" or "labels" can be included.`, prefix))
+ }
+ if len(prof.Labels) > 0 {
+ customSettings[i].LabelsIncludeAll = customSettings[i].Labels
+ customSettings[i].Labels = nil
+ }
+ }
+}
+
func (svc *Service) validateTeamCalendarIntegrations(
calendarIntegration *fleet.TeamGoogleCalendarIntegration,
appCfg *fleet.AppConfig, dryRun bool, invalid *fleet.InvalidArgumentError,
diff --git a/frontend/interfaces/config.ts b/frontend/interfaces/config.ts
index 2cca3ea365..f52cfd3925 100644
--- a/frontend/interfaces/config.ts
+++ b/frontend/interfaces/config.ts
@@ -29,6 +29,12 @@ export interface IMacOsMigrationSettings {
webhook_url: string;
}
+interface ICustomSetting {
+ path: string;
+ labels_include_all?: string[];
+ labels_exclude_any?: string[];
+}
+
export interface IMdmConfig {
enable_disk_encryption: boolean;
enabled_and_configured: boolean;
@@ -42,7 +48,7 @@ export interface IMdmConfig {
deadline: string | null;
};
macos_settings: {
- custom_settings: null;
+ custom_settings: null | ICustomSetting[];
enable_disk_encryption: boolean;
};
macos_setup: {
diff --git a/frontend/interfaces/dropdownOption.ts b/frontend/interfaces/dropdownOption.ts
index a8895d48b9..67b1bacd4d 100644
--- a/frontend/interfaces/dropdownOption.ts
+++ b/frontend/interfaces/dropdownOption.ts
@@ -1,3 +1,4 @@
+import { ReactNode } from "react";
import PropTypes from "prop-types";
export default PropTypes.shape({
@@ -10,6 +11,7 @@ export interface IDropdownOption {
disabled?: boolean;
label: string | JSX.Element;
value: string | number;
+ helpText?: ReactNode;
premiumOnly?: boolean;
tooltipContent?: string | JSX.Element;
}
diff --git a/frontend/interfaces/mdm.ts b/frontend/interfaces/mdm.ts
index 4b3a8791e6..a911f0c9e7 100644
--- a/frontend/interfaces/mdm.ts
+++ b/frontend/interfaces/mdm.ts
@@ -71,7 +71,8 @@ export type ProfilePlatform = "darwin" | "windows";
export interface IProfileLabel {
name: string;
- broken: boolean;
+ id?: number; // id is only present when the label is not broken
+ broken?: boolean;
}
export interface IMdmProfile {
@@ -83,7 +84,8 @@ export interface IMdmProfile {
created_at: string;
updated_at: string;
checksum: string | null; // null for windows profiles
- labels?: IProfileLabel[];
+ labels_include_all?: IProfileLabel[];
+ labels_exclude_any?: IProfileLabel[];
}
export type MdmProfileStatus = "verified" | "verifying" | "pending" | "failed";
diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx
index 8298a10647..89887e9b83 100644
--- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx
+++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/CustomSettings.tsx
@@ -20,7 +20,7 @@ import Pagination from "pages/ManageControlsPage/components/Pagination";
import UploadList from "../../../components/UploadList";
import AddProfileCard from "./components/ProfileUploader/components/AddProfileCard";
-import AddProfileModal from "./components/ProfileUploader/components/AddProfileModal";
+import AddProfileModal from "./components/ProfileUploader/components/AddProfileModal/AddProfileModal";
import DeleteProfileModal from "./components/DeleteProfileModal/DeleteProfileModal";
import ProfileLabelsModal from "./components/ProfileLabelsModal/ProfileLabelsModal";
import ProfileListItem from "./components/ProfileListItem";
@@ -101,7 +101,7 @@ const CustomSettings = ({
onMutation();
renderFlash("success", "Successfully deleted!");
} catch (e) {
- renderFlash("error", "Couldn’t delete. Please try again.");
+ renderFlash("error", "Couldn't delete. Please try again.");
} finally {
selectedProfile.current = null;
setShowDeleteProfileModal(false);
@@ -169,6 +169,10 @@ const CustomSettings = ({
);
};
+ const hasLabels =
+ !!profileLabelsModalData?.labels_include_all?.length ||
+ !!profileLabelsModalData?.labels_exclude_any?.length;
+
return (
@@ -189,7 +193,6 @@ const CustomSettings = ({
)}
{showAddProfileModal && (
)}
- {!!isPremiumTier && !!profileLabelsModalData?.labels?.length && (
+ {isPremiumTier && hasLabels && (
(
-
- {profileName} will only be applied to hosts that have all these
- labels:
-
-);
+ targetType,
+}: IModalDescriptionProps) => {
+ const targetTypeText =
+ targetType === "includeAll" ? (
+ <>
+ have all
+ >
+ ) : (
+ <>
+ don't have any
+ >
+ );
+
+ return (
+
+ {profileName} profile only applies to hosts that {targetTypeText}{" "}
+ of these labels:
+
+ );
+};
const BrokenLabelWarning = () => (
The configuration profile is{" "}
broken
@@ -67,7 +84,14 @@ const ProfileLabelsModal = ({
profile,
setModalData,
}: IProfileLabelsModalProps) => {
- if (!profile?.labels?.length) {
+ if (!profile) {
+ return null;
+ }
+
+ const { name, labels_include_all, labels_exclude_any } = profile;
+ const labels = labels_include_all || labels_exclude_any;
+
+ if (!labels?.length) {
// caller ensures this never happens
return null;
}
@@ -75,9 +99,13 @@ const ProfileLabelsModal = ({
return (
setModalData(null)}>
- {profile.labels.some((label) => label.broken) &&
}
-
-
+ {labels.some((label) => label.broken) &&
}
+
+