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) && } + +