From 18b31701e3a2d1bc85dc599ff4f4dc0638d4cbc8 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 25 Aug 2021 08:52:15 -0400 Subject: [PATCH] [APM] update policy editor with additional config values (#109516) (#109975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactoring apm integration sections * adding agent auth section * refactoring * adding some unit tests * fixing ts issue * removing unnecessary section * hide fields * removing suggestions when combo * fixing apm migration * addressing PR comments * addressing PR changes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: CauĂȘ Marcondes <55978943+cauemarcondes@users.noreply.github.com> --- .../create_apm_policy_form.tsx | 8 +- .../apm_policy_form/edit_apm_policy_form.tsx | 4 +- .../apm_policy_form/index.tsx | 126 +++++++++-- .../apm_policy_form/settings/rum_settings.tsx | 197 ------------------ .../apm_policy_form/settings/tls_settings.tsx | 117 ----------- .../apm_policy_form/settings/typings.ts | 38 ---- .../agent_authorization_settings.test.ts | 70 +++++++ .../agent_authorization_settings.ts | 166 +++++++++++++++ .../settings_definition/apm_settings.test.ts | 87 ++++++++ .../apm_settings.ts} | 109 ++-------- .../settings_definition/rum_settings.ts | 132 ++++++++++++ .../settings_definition/tls_settings.test.ts | 36 ++++ .../settings_definition/tls_settings.ts | 95 +++++++++ .../form_row_setting.tsx | 33 ++- .../index.tsx} | 114 +++++----- .../{settings => settings_form}/utils.test.ts | 58 ++++-- .../{settings => settings_form}/utils.ts | 17 +- .../apm_policy_form/typings.ts | 35 +++- .../public/tutorial/config_agent/index.tsx | 2 +- .../tutorial_fleet_instructions/index.tsx | 2 +- .../get_apm_package_policy_definition.ts | 58 +++--- 21 files changed, 910 insertions(+), 594 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/rum_settings.tsx delete mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/tls_settings.tsx delete mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/typings.ts create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.test.ts create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.ts create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.test.ts rename x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/{settings/apm_settings.tsx => settings_definition/apm_settings.ts} (70%) create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/rum_settings.ts create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.test.ts create mode 100644 x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.ts rename x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/{settings => settings_form}/form_row_setting.tsx (72%) rename x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/{settings/settings_form.tsx => settings_form/index.tsx} (56%) rename x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/{settings => settings_form}/utils.test.ts (73%) rename x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/{settings => settings_form}/utils.ts (80%) diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/create_apm_policy_form.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/create_apm_policy_form.tsx index 6a970632ee19..7354846aba64 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/create_apm_policy_form.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/create_apm_policy_form.tsx @@ -21,7 +21,7 @@ export function CreateAPMPolicyForm({ newPolicy, onChange }: Props) { const [firstInput, ...restInputs] = newPolicy?.inputs; const vars = firstInput?.vars; - function handleChange(newVars: PackagePolicyVars, isValid: boolean) { + function updateAPMPolicy(newVars: PackagePolicyVars, isValid: boolean) { onChange({ isValid, updatedPolicy: { @@ -31,6 +31,10 @@ export function CreateAPMPolicyForm({ newPolicy, onChange }: Props) { }); } return ( - + ); } diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.tsx index 5843b9005e7f..e8d3b5d6940a 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/edit_apm_policy_form.tsx @@ -24,7 +24,7 @@ export function EditAPMPolicyForm({ newPolicy, onChange }: Props) { const [firstInput, ...restInputs] = newPolicy?.inputs; const vars = firstInput?.vars; - function handleChange(newVars: PackagePolicyVars, isValid: boolean) { + function updateAPMPolicy(newVars: PackagePolicyVars, isValid: boolean) { onChange({ isValid, updatedPolicy: { @@ -35,7 +35,7 @@ export function EditAPMPolicyForm({ newPolicy, onChange }: Props) { return ( ); diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx index 60d95ab2b9f0..51944fdbddec 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/index.tsx @@ -5,30 +5,124 @@ * 2.0. */ import { EuiSpacer } from '@elastic/eui'; -import React from 'react'; -import { OnFormChangeFn, PackagePolicyVars } from './typings'; -import { APMSettingsForm } from './settings/apm_settings'; -import { RUMSettingsForm } from './settings/rum_settings'; -import { TLSSettingsForm } from './settings/tls_settings'; +import { i18n } from '@kbn/i18n'; +import React, { useMemo } from 'react'; +import { getAgentAuthorizationSettings } from './settings_definition/agent_authorization_settings'; +import { getApmSettings } from './settings_definition/apm_settings'; +import { + getRUMSettings, + isRUMFormValid, +} from './settings_definition/rum_settings'; +import { + getTLSSettings, + isTLSFormValid, +} from './settings_definition/tls_settings'; +import { SettingsForm, SettingsSection } from './settings_form'; +import { isSettingsFormValid, mergeNewVars } from './settings_form/utils'; +import { PackagePolicyVars } from './typings'; interface Props { - onChange: OnFormChangeFn; + updateAPMPolicy: (newVars: PackagePolicyVars, isValid: boolean) => void; vars?: PackagePolicyVars; isCloudPolicy: boolean; } -export function APMPolicyForm({ vars = {}, isCloudPolicy, onChange }: Props) { +export function APMPolicyForm({ + vars = {}, + isCloudPolicy, + updateAPMPolicy, +}: Props) { + const { + apmSettings, + rumSettings, + tlsSettings, + agentAuthorizationSettings, + } = useMemo(() => { + return { + apmSettings: getApmSettings({ isCloudPolicy }), + rumSettings: getRUMSettings(), + tlsSettings: getTLSSettings(), + agentAuthorizationSettings: getAgentAuthorizationSettings({ + isCloudPolicy, + }), + }; + }, [isCloudPolicy]); + + function handleFormChange(key: string, value: any) { + // Merge new key/value with the rest of fields + const newVars = mergeNewVars(vars, key, value); + + // Validate the entire form before sending it to fleet + const isFormValid = + isSettingsFormValid(apmSettings, newVars) && + isRUMFormValid(newVars, rumSettings) && + isTLSFormValid(newVars, tlsSettings) && + isSettingsFormValid(agentAuthorizationSettings, newVars); + + updateAPMPolicy(newVars, isFormValid); + } + + const settingsSections: SettingsSection[] = [ + { + id: 'apm', + title: i18n.translate( + 'xpack.apm.fleet_integration.settings.apm.settings.title', + { defaultMessage: 'General' } + ), + subtitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.apm.settings.subtitle', + { defaultMessage: 'Settings for the APM integration.' } + ), + settings: apmSettings, + }, + { + id: 'rum', + title: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.settings.title', + { defaultMessage: 'Real User Monitoring' } + ), + subtitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.settings.subtitle', + { defaultMessage: 'Manage the configuration of the RUM JS agent.' } + ), + settings: rumSettings, + }, + { + id: 'tls', + title: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.settings.title', + { defaultMessage: 'TLS Settings' } + ), + subtitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.settings.subtitle', + { defaultMessage: 'Settings for TLS certification.' } + ), + settings: tlsSettings, + }, + { + id: 'agentAuthorization', + title: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.settings.title', + { defaultMessage: 'Agent authorization' } + ), + settings: agentAuthorizationSettings, + }, + ]; + return ( <> - - - - - + {settingsSections.map((settingsSection) => { + return ( + + + + + ); + })} ); } diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/rum_settings.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/rum_settings.tsx deleted file mode 100644 index 0a1b6c66a47e..000000000000 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/rum_settings.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { getIntegerRt } from '../../../../../common/agent_configuration/runtime_types/integer_rt'; -import { OnFormChangeFn, PackagePolicyVars } from '../typings'; -import { SettingsForm } from './settings_form'; -import { SettingDefinition } from './typings'; -import { isSettingsFormValid, mergeNewVars, OPTIONAL_LABEL } from './utils'; - -const ENABLE_RUM_KEY = 'enable_rum'; -const rumSettings: SettingDefinition[] = [ - { - key: ENABLE_RUM_KEY, - type: 'boolean', - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.enableRumTitle', - { defaultMessage: 'Enable RUM' } - ), - rowDescription: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.enableRumDescription', - { defaultMessage: 'Enable Real User Monitoring (RUM)' } - ), - settings: [ - { - key: 'rum_allow_headers', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderLabel', - { defaultMessage: 'Allowed origin headers' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderHelpText', - { - defaultMessage: 'Allowed Origin headers to be sent by User Agents.', - } - ), - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderTitle', - { defaultMessage: 'Custom headers' } - ), - rowDescription: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderDescription', - { defaultMessage: 'Configure authentication for the agent' } - ), - }, - { - key: 'rum_allow_origins', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowOriginsLabel', - { defaultMessage: 'Access-Control-Allow-Headers' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowOriginsHelpText', - { - defaultMessage: - 'Supported Access-Control-Allow-Headers in addition to "Content-Type", "Content-Encoding" and "Accept".', - } - ), - }, - { - key: 'rum_response_headers', - type: 'area', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumResponseHeadersLabel', - { defaultMessage: 'Custom HTTP response headers' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumResponseHeadersHelpText', - { - defaultMessage: - 'Added to RUM responses, e.g. for security policy compliance.', - } - ), - }, - { - type: 'advanced_settings', - settings: [ - { - key: 'rum_event_rate_limit', - type: 'integer', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLimitLabel', - { defaultMessage: 'Rate limit events per IP' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLimitHelpText', - { - defaultMessage: - 'Maximum number of events allowed per IP per second.', - } - ), - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLimitTitle', - { defaultMessage: 'Limits' } - ), - rowDescription: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLimitDescription', - { defaultMessage: 'Configure authentication for the agent' } - ), - validation: getIntegerRt({ min: 1 }), - }, - { - key: 'rum_event_rate_lru_size', - type: 'integer', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLRUSizeLabel', - { defaultMessage: 'Rate limit cache size' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumEventRateLRUSizeHelpText', - { - defaultMessage: - 'Number of unique IPs to be cached for the rate limiter.', - } - ), - validation: getIntegerRt({ min: 1 }), - }, - { - key: 'rum_library_pattern', - type: 'text', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumLibraryPatternLabel', - { defaultMessage: 'Library Frame Pattern' } - ), - labelAppend: OPTIONAL_LABEL, - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumLibraryPatternHelpText', - { - defaultMessage: - "Identify library frames by matching a stacktrace frame's file_name and abs_path against this regexp.", - } - ), - }, - { - key: 'rum_allow_service_names', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowServiceNamesLabel', - { defaultMessage: 'Allowed service names' } - ), - labelAppend: OPTIONAL_LABEL, - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowServiceNamesTitle', - { defaultMessage: 'Allowed service names' } - ), - rowDescription: i18n.translate( - 'xpack.apm.fleet_integration.settings.rum.rumAllowServiceNamesDescription', - { defaultMessage: 'Configure authentication for the agent' } - ), - }, - ], - }, - ], - }, -]; - -interface Props { - vars: PackagePolicyVars; - onChange: OnFormChangeFn; -} - -export function RUMSettingsForm({ vars, onChange }: Props) { - return ( - { - const newVars = mergeNewVars(vars, key, value); - onChange( - newVars, - // only validates RUM when its flag is enabled - !newVars[ENABLE_RUM_KEY].value || - isSettingsFormValid(rumSettings, newVars) - ); - }} - /> - ); -} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/tls_settings.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/tls_settings.tsx deleted file mode 100644 index 6529de07b756..000000000000 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/tls_settings.tsx +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { OnFormChangeFn, PackagePolicyVars } from '../typings'; -import { SettingsForm } from './settings_form'; -import { SettingDefinition } from './typings'; -import { - isSettingsFormValid, - mergeNewVars, - OPTIONAL_LABEL, - REQUIRED_LABEL, -} from './utils'; - -const TLS_ENABLED_KEY = 'tls_enabled'; -const tlsSettings: SettingDefinition[] = [ - { - key: TLS_ENABLED_KEY, - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsEnabledTitle', - { defaultMessage: 'Enable TLS' } - ), - type: 'boolean', - settings: [ - { - key: 'tls_certificate', - type: 'text', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsCertificateLabel', - { defaultMessage: 'File path to server certificate' } - ), - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsCertificateTitle', - { defaultMessage: 'TLS certificate' } - ), - labelAppend: REQUIRED_LABEL, - required: true, - }, - { - key: 'tls_key', - type: 'text', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsKeyLabel', - { defaultMessage: 'File path to server certificate key' } - ), - labelAppend: REQUIRED_LABEL, - required: true, - }, - { - key: 'tls_supported_protocols', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsSupportedProtocolsLabel', - { defaultMessage: 'Supported protocol versions' } - ), - labelAppend: OPTIONAL_LABEL, - }, - { - key: 'tls_cipher_suites', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsCipherSuitesLabel', - { defaultMessage: 'Cipher suites for TLS connections' } - ), - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsCipherSuitesHelpText', - { defaultMessage: 'Not configurable for TLS 1.3.' } - ), - labelAppend: OPTIONAL_LABEL, - }, - { - key: 'tls_curve_types', - type: 'combo', - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.tls.tlsCurveTypesLabel', - { defaultMessage: 'Curve types for ECDHE based cipher suites' } - ), - labelAppend: OPTIONAL_LABEL, - }, - ], - }, -]; - -interface Props { - vars: PackagePolicyVars; - onChange: OnFormChangeFn; -} - -export function TLSSettingsForm({ vars, onChange }: Props) { - return ( - { - const newVars = mergeNewVars(vars, key, value); - onChange( - newVars, - // only validates TLS when its flag is enabled - !newVars[TLS_ENABLED_KEY].value || - isSettingsFormValid(tlsSettings, newVars) - ); - }} - /> - ); -} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/typings.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/typings.ts deleted file mode 100644 index 8f2e28a72ea2..000000000000 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/typings.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import * as t from 'io-ts'; - -export type SettingValidation = t.Type; - -interface AdvancedSettings { - type: 'advanced_settings'; - settings: SettingDefinition[]; -} - -export interface Setting { - type: - | 'text' - | 'combo' - | 'area' - | 'boolean' - | 'integer' - | 'bytes' - | 'duration'; - key: string; - rowTitle?: string; - rowDescription?: string; - label?: string; - helpText?: string; - placeholder?: string; - labelAppend?: string; - settings?: SettingDefinition[]; - validation?: SettingValidation; - required?: boolean; - readOnly?: boolean; -} - -export type SettingDefinition = Setting | AdvancedSettings; diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.test.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.test.ts new file mode 100644 index 000000000000..509b0d13552c --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.test.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAgentAuthorizationSettings } from './agent_authorization_settings'; +import { SettingsRow } from '../typings'; +import { isSettingsFormValid } from '../settings_form/utils'; + +describe('apm-fleet-apm-integration', () => { + describe('getAgentAuthorizationSettings', () => { + function findSetting(key: string, settings: SettingsRow[]) { + return settings.find( + (setting) => setting.type !== 'advanced_setting' && setting.key === key + ); + } + it('returns read only secret token when on cloud', () => { + const settings = getAgentAuthorizationSettings({ isCloudPolicy: true }); + const secretToken = findSetting('secret_token', settings); + expect(secretToken).toEqual({ + type: 'text', + key: 'secret_token', + readOnly: true, + labelAppend: 'Optional', + label: 'Secret token', + }); + }); + it('returns secret token when NOT on cloud', () => { + const settings = getAgentAuthorizationSettings({ isCloudPolicy: false }); + const secretToken = findSetting('secret_token', settings); + + expect(secretToken).toEqual({ + type: 'text', + key: 'secret_token', + readOnly: false, + labelAppend: 'Optional', + label: 'Secret token', + }); + }); + }); + + describe('isAgentAuthorizationFormValid', () => { + describe('validates integer fields', () => { + [ + 'api_key_limit', + 'anonymous_rate_limit_ip_limit', + 'anonymous_rate_limit_event_limit', + ].map((key) => { + it(`returns false when ${key} is lower than 1`, () => { + const settings = getAgentAuthorizationSettings({ + isCloudPolicy: true, + }); + expect( + isSettingsFormValid(settings, { + [key]: { value: 0, type: 'integer' }, + }) + ).toBeFalsy(); + + expect( + isSettingsFormValid(settings, { + [key]: { value: -1, type: 'integer' }, + }) + ).toBeFalsy(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.ts new file mode 100644 index 000000000000..3540fb97fb17 --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/agent_authorization_settings.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { getIntegerRt } from '../../../../../common/agent_configuration/runtime_types/integer_rt'; +import { OPTIONAL_LABEL } from '../settings_form/utils'; +import { SettingsRow } from '../typings'; + +export function getAgentAuthorizationSettings({ + isCloudPolicy, +}: { + isCloudPolicy: boolean; +}): SettingsRow[] { + return [ + { + type: 'boolean', + key: 'api_key_enabled', + labelAppend: OPTIONAL_LABEL, + placeholder: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyAuthenticationPlaceholder', + { defaultMessage: 'API key for agent authentication' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyAuthenticationHelpText', + { + defaultMessage: + 'Enable API Key auth between APM Server and APM Agents.', + } + ), + settings: [ + { + key: 'api_key_limit', + type: 'integer', + labelAppend: OPTIONAL_LABEL, + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyLimitLabel', + { defaultMessage: 'Number of keys' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyLimitHelpText', + { defaultMessage: 'Might be used for security policy compliance.' } + ), + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyLimitTitle', + { + defaultMessage: + 'Maximum number of API keys of Agent authentication', + } + ), + rowDescription: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.apiKeyLimitDescription', + { + defaultMessage: + 'Restrict number of unique API keys per minute, used for auth between APM Agents and Server.', + } + ), + validation: getIntegerRt({ min: 1 }), + }, + ], + }, + { + type: 'text', + key: 'secret_token', + readOnly: isCloudPolicy, + labelAppend: OPTIONAL_LABEL, + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.secretTokenLabel', + { defaultMessage: 'Secret token' } + ), + }, + { + type: 'boolean', + key: 'anonymous_enabled', + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousEnabledTitle', + { defaultMessage: 'Anonymous Agent access' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousEnabledHelpText', + { + defaultMessage: + 'Enable anonymous access to APM Server for select APM Agents.', + } + ), + rowDescription: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousEnabledDescription', + { + defaultMessage: + 'Allow anonymous access only for specified agents and/or services. This is primarily intended to allow limited access for untrusted agents, such as Real User Monitoring. When anonymous auth is enabled, only agents matching the Allowed Agents and services matching the Allowed Services configuration are allowed. See below for details on default values.', + } + ), + settings: [ + { + type: 'combo', + key: 'anonymous_allow_agent', + labelAppend: OPTIONAL_LABEL, + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentLabel', + { defaultMessage: 'Allowed agents' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowAgentHelpText', + { + defaultMessage: 'Allowed agent names for anonymous access.', + } + ), + }, + { + type: 'combo', + key: 'anonymous_allow_service', + labelAppend: OPTIONAL_LABEL, + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowServiceLabel', + { defaultMessage: 'Allowed services' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousAllowServiceHelpText', + { + defaultMessage: 'Allowed service names for anonymous access.', + } + ), + }, + { + key: 'anonymous_rate_limit_ip_limit', + type: 'integer', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousRateLimitIpLimitLabel', + { defaultMessage: 'Rate limit (IP limit)' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousRateLimitIpLimitHelpText', + { + defaultMessage: + 'Number of unique client IPs for which a distinct rate limit will be maintained.', + } + ), + validation: getIntegerRt({ min: 1 }), + }, + { + key: 'anonymous_rate_limit_event_limit', + type: 'integer', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousRateLimitEventLimitLabel', + { + defaultMessage: 'Event rate limit (event limit)', + } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.agentAuthorization.anonymousRateLimitEventLimitHelpText', + { + defaultMessage: + 'Maximum number of events per client IP per second.', + } + ), + validation: getIntegerRt({ min: 1 }), + }, + ], + }, + ]; +} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.test.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.test.ts new file mode 100644 index 000000000000..2d2acbcd37c5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getApmSettings } from './apm_settings'; +import { SettingsRow, BasicSettingRow } from '../typings'; +import { isSettingsFormValid } from '../settings_form/utils'; + +describe('apm_settings', () => { + describe('getApmSettings', () => { + function findSetting(key: string, settings: SettingsRow[]) { + return settings.find( + (setting) => setting.type !== 'advanced_setting' && setting.key === key + ) as BasicSettingRow; + } + ['host', 'url'].map((key) => { + it(`returns read only ${key} when on cloud`, () => { + const settings = getApmSettings({ isCloudPolicy: true }); + const setting = findSetting(key, settings); + expect(setting.readOnly).toBeTruthy(); + }); + it(`returns ${key} when NOT on cloud`, () => { + const settings = getApmSettings({ isCloudPolicy: false }); + const setting = findSetting(key, settings); + expect(setting.readOnly).toBeFalsy(); + }); + }); + }); + + describe('isAPMFormValid', () => { + describe('validates integer fields', () => { + ['max_header_bytes', 'max_event_bytes'].map((key) => { + it(`returns false when ${key} is lower than 1`, () => { + const settings = getApmSettings({ isCloudPolicy: true }); + expect( + isSettingsFormValid(settings, { + [key]: { value: 0, type: 'integer' }, + }) + ).toBeFalsy(); + + expect( + isSettingsFormValid(settings, { + [key]: { value: -1, type: 'integer' }, + }) + ).toBeFalsy(); + }); + }); + ['max_connections'].map((key) => { + it(`returns false when ${key} is lower than 0`, () => { + const settings = getApmSettings({ isCloudPolicy: true }); + expect( + isSettingsFormValid(settings, { + [key]: { value: -1, type: 'integer' }, + }) + ).toBeFalsy(); + }); + }); + }); + + describe('validates required fields', () => { + ['host', 'url'].map((key) => { + it(`return false when ${key} is not defined`, () => { + const settings = getApmSettings({ isCloudPolicy: true }); + expect(isSettingsFormValid(settings, {})).toBeFalsy(); + }); + }); + }); + + describe('validates duration fields', () => { + ['idle_timeout', 'read_timeout', 'shutdown_timeout', 'write_timeout'].map( + (key) => { + it(`return false when ${key} lower then 1ms`, () => { + const settings = getApmSettings({ isCloudPolicy: true }); + expect( + isSettingsFormValid(settings, { + [key]: { value: '0ms', type: 'text' }, + }) + ).toBeFalsy(); + }); + } + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/apm_settings.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.ts similarity index 70% rename from x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/apm_settings.tsx rename to x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.ts index d80bccc529d6..2b88e1a1df9e 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/apm_settings.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/apm_settings.ts @@ -5,26 +5,16 @@ * 2.0. */ import { i18n } from '@kbn/i18n'; -import React, { useMemo } from 'react'; import { getDurationRt } from '../../../../../common/agent_configuration/runtime_types/duration_rt'; import { getIntegerRt } from '../../../../../common/agent_configuration/runtime_types/integer_rt'; -import { OnFormChangeFn, PackagePolicyVars } from '../typings'; -import { SettingsForm } from './settings_form'; -import { SettingDefinition } from './typings'; -import { - isSettingsFormValid, - mergeNewVars, - OPTIONAL_LABEL, - REQUIRED_LABEL, -} from './utils'; +import { OPTIONAL_LABEL, REQUIRED_LABEL } from '../settings_form/utils'; +import { SettingsRow } from '../typings'; -interface Props { - vars: PackagePolicyVars; - onChange: OnFormChangeFn; +export function getApmSettings({ + isCloudPolicy, +}: { isCloudPolicy: boolean; -} - -function getApmSettings(isCloudPolicy: boolean): SettingDefinition[] { +}): SettingsRow[] { return [ { type: 'text', @@ -63,33 +53,7 @@ function getApmSettings(isCloudPolicy: boolean): SettingDefinition[] { required: true, }, { - type: 'text', - key: 'secret_token', - readOnly: isCloudPolicy, - labelAppend: OPTIONAL_LABEL, - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.secretTokenLabel', - { defaultMessage: 'Secret token' } - ), - }, - { - type: 'boolean', - key: 'api_key_enabled', - labelAppend: OPTIONAL_LABEL, - placeholder: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyAuthenticationPlaceholder', - { defaultMessage: 'API key for agent authentication' } - ), - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyAuthenticationHelpText', - { - defaultMessage: - 'Enable API Key auth between APM Server and APM Agents.', - } - ), - }, - { - type: 'advanced_settings', + type: 'advanced_setting', settings: [ { key: 'max_header_bytes', @@ -176,7 +140,11 @@ function getApmSettings(isCloudPolicy: boolean): SettingDefinition[] { 'xpack.apm.fleet_integration.settings.apm.maxConnectionsLabel', { defaultMessage: 'Simultaneously accepted connections' } ), - validation: getIntegerRt({ min: 1 }), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.apm.maxConnectionsHelpText', + { defaultMessage: '0 for unlimited' } + ), + validation: getIntegerRt({ min: 0 }), }, { key: 'response_headers', @@ -202,34 +170,6 @@ function getApmSettings(isCloudPolicy: boolean): SettingDefinition[] { } ), }, - { - key: 'api_key_limit', - type: 'integer', - labelAppend: OPTIONAL_LABEL, - label: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyLimitLabel', - { defaultMessage: 'Number of keys' } - ), - helpText: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyLimitHelpText', - { defaultMessage: 'Might be used for security policy compliance.' } - ), - rowTitle: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyLimitTitle', - { - defaultMessage: - 'Maximum number of API keys of Agent authentication', - } - ), - rowDescription: i18n.translate( - 'xpack.apm.fleet_integration.settings.apm.apiKeyLimitDescription', - { - defaultMessage: - 'Restrict number of unique API keys per minute, used for auth between aPM Agents and Server.', - } - ), - validation: getIntegerRt({ min: 1 }), - }, { key: 'capture_personal_data', type: 'boolean', @@ -279,28 +219,3 @@ function getApmSettings(isCloudPolicy: boolean): SettingDefinition[] { }, ]; } - -export function APMSettingsForm({ vars, onChange, isCloudPolicy }: Props) { - const apmSettings = useMemo(() => { - return getApmSettings(isCloudPolicy); - }, [isCloudPolicy]); - - return ( - { - const newVars = mergeNewVars(vars, key, value); - onChange(newVars, isSettingsFormValid(apmSettings, newVars)); - }} - /> - ); -} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/rum_settings.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/rum_settings.ts new file mode 100644 index 000000000000..f32969d248b9 --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/rum_settings.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { PackagePolicyVars, SettingsRow } from '../typings'; +import { isSettingsFormValid, OPTIONAL_LABEL } from '../settings_form/utils'; + +const ENABLE_RUM_KEY = 'enable_rum'; +export function getRUMSettings(): SettingsRow[] { + return [ + { + key: ENABLE_RUM_KEY, + type: 'boolean', + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.enableRumTitle', + { defaultMessage: 'Enable RUM' } + ), + rowDescription: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.enableRumDescription', + { defaultMessage: 'Enable Real User Monitoring (RUM)' } + ), + settings: [ + { + key: 'rum_allow_origins', + type: 'combo', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowOriginsLabel', + { defaultMessage: 'Origin Headers' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowOriginsHelpText', + { + defaultMessage: + 'Allowed Origin headers to be sent by User Agents.', + } + ), + }, + { + key: 'rum_allow_headers', + type: 'combo', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderLabel', + { defaultMessage: 'Access-Control-Allow-Headers' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderHelpText', + { + defaultMessage: + 'Supported Access-Control-Allow-Headers in addition to "Content-Type", "Content-Encoding" and "Accept".', + } + ), + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderTitle', + { defaultMessage: 'Custom headers' } + ), + rowDescription: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumAllowHeaderDescription', + { defaultMessage: 'Configure authentication for the agent' } + ), + }, + { + key: 'rum_response_headers', + type: 'area', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumResponseHeadersLabel', + { defaultMessage: 'Custom HTTP response headers' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumResponseHeadersHelpText', + { + defaultMessage: + 'Added to RUM responses, e.g. for security policy compliance.', + } + ), + }, + { + type: 'advanced_setting', + settings: [ + { + key: 'rum_library_pattern', + type: 'text', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumLibraryPatternLabel', + { defaultMessage: 'Library Frame Pattern' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumLibraryPatternHelpText', + { + defaultMessage: + "Identify library frames by matching a stacktrace frame's file_name and abs_path against this regexp.", + } + ), + }, + { + key: 'rum_exclude_from_grouping', + type: 'text', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumExcludeFromGroupingLabel', + { defaultMessage: 'Exclude from grouping' } + ), + labelAppend: OPTIONAL_LABEL, + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.rum.rumExcludeFromGroupingHelpText', + { + defaultMessage: + "Exclude stacktrace frames from error group calculations by matching a stacktrace frame's `file_name` against this regexp.", + } + ), + }, + ], + }, + ], + }, + ]; +} + +export function isRUMFormValid( + newVars: PackagePolicyVars, + rumSettings: SettingsRow[] +) { + // only validates RUM when its flag is enabled + return ( + !newVars[ENABLE_RUM_KEY].value || isSettingsFormValid(rumSettings, newVars) + ); +} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.test.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.test.ts new file mode 100644 index 000000000000..7043a37fd0e5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getTLSSettings, isTLSFormValid } from './tls_settings'; + +describe('tls_settings', () => { + describe('isTLSFormValid', () => { + describe('validates duration fields', () => { + ['tls_certificate', 'tls_key'].map((key) => { + it(`return false when ${key} lower then 1ms`, () => { + const settings = getTLSSettings(); + expect( + isTLSFormValid( + { tls_enabled: { value: true, type: 'bool' } }, + settings + ) + ).toBeFalsy(); + }); + }); + }); + + it('returns true when tls_enabled is disabled', () => { + const settings = getTLSSettings(); + expect( + isTLSFormValid( + { tls_enabled: { value: false, type: 'bool' } }, + settings + ) + ).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.ts new file mode 100644 index 000000000000..6e699057ad3e --- /dev/null +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_definition/tls_settings.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { PackagePolicyVars, SettingsRow } from '../typings'; +import { + isSettingsFormValid, + OPTIONAL_LABEL, + REQUIRED_LABEL, +} from '../settings_form/utils'; + +const TLS_ENABLED_KEY = 'tls_enabled'; + +export function getTLSSettings(): SettingsRow[] { + return [ + { + key: TLS_ENABLED_KEY, + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsEnabledTitle', + { defaultMessage: 'Enable TLS' } + ), + type: 'boolean', + settings: [ + { + key: 'tls_certificate', + type: 'text', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsCertificateLabel', + { defaultMessage: 'File path to server certificate' } + ), + rowTitle: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsCertificateTitle', + { defaultMessage: 'TLS certificate' } + ), + labelAppend: REQUIRED_LABEL, + required: true, + }, + { + key: 'tls_key', + type: 'text', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsKeyLabel', + { defaultMessage: 'File path to server certificate key' } + ), + labelAppend: REQUIRED_LABEL, + required: true, + }, + { + key: 'tls_supported_protocols', + type: 'combo', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsSupportedProtocolsLabel', + { defaultMessage: 'Supported protocol versions' } + ), + labelAppend: OPTIONAL_LABEL, + }, + { + key: 'tls_cipher_suites', + type: 'combo', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsCipherSuitesLabel', + { defaultMessage: 'Cipher suites for TLS connections' } + ), + helpText: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsCipherSuitesHelpText', + { defaultMessage: 'Not configurable for TLS 1.3.' } + ), + labelAppend: OPTIONAL_LABEL, + }, + { + key: 'tls_curve_types', + type: 'combo', + label: i18n.translate( + 'xpack.apm.fleet_integration.settings.tls.tlsCurveTypesLabel', + { defaultMessage: 'Curve types for ECDHE based cipher suites' } + ), + labelAppend: OPTIONAL_LABEL, + }, + ], + }, + ]; +} + +export function isTLSFormValid( + newVars: PackagePolicyVars, + tlsSettings: SettingsRow[] +) { + // only validates TLS when its flag is enabled + return ( + !newVars[TLS_ENABLED_KEY].value || isSettingsFormValid(tlsSettings, newVars) + ); +} diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/form_row_setting.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/form_row_setting.tsx similarity index 72% rename from x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/form_row_setting.tsx rename to x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/form_row_setting.tsx index 33aeaccbeb8c..6b3d0ed776dc 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/form_row_setting.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/form_row_setting.tsx @@ -15,11 +15,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { FormRowOnChange } from './settings_form'; -import { SettingDefinition } from './typings'; +import { FormRowOnChange } from './'; +import { SettingsRow } from '../typings'; interface Props { - setting: SettingDefinition; + row: SettingsRow; value?: any; onChange: FormRowOnChange; } @@ -33,17 +33,15 @@ const DISABLED_LABEL = i18n.translate( { defaultMessage: 'Disabled' } ); -export function FormRowSetting({ setting, value, onChange }: Props) { - switch (setting.type) { +export function FormRowSetting({ row, value, onChange }: Props) { + switch (row.type) { case 'boolean': { return ( { - onChange(setting.key, e.target.checked); + onChange(row.key, e.target.checked); }} /> ); @@ -52,11 +50,11 @@ export function FormRowSetting({ setting, value, onChange }: Props) { case 'text': { return ( : undefined} + prepend={row.readOnly ? : undefined} onChange={(e) => { - onChange(setting.key, e.target.value); + onChange(row.key, e.target.value); }} /> ); @@ -66,7 +64,7 @@ export function FormRowSetting({ setting, value, onChange }: Props) { { - onChange(setting.key, e.target.value); + onChange(row.key, e.target.value); }} /> ); @@ -77,7 +75,7 @@ export function FormRowSetting({ setting, value, onChange }: Props) { { - onChange(setting.key, e.target.value); + onChange(row.key, e.target.value); }} /> ); @@ -88,6 +86,7 @@ export function FormRowSetting({ setting, value, onChange }: Props) { : []; return ( { onChange( - setting.key, + row.key, option.map(({ label }) => label) ); }} onCreateOption={(newOption) => { - onChange(setting.key, [...value, newOption]); + onChange(row.key, [...value, newOption]); }} isClearable={true} /> ); } default: - throw new Error(`Unknown type "${setting.type}"`); + throw new Error(`Unknown type "${row.type}"`); } } diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/settings_form.tsx b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx similarity index 56% rename from x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/settings_form.tsx rename to x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx index 1b1dcddc0e7f..af78e885e85d 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/settings_form.tsx +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/index.tsx @@ -17,9 +17,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { PackagePolicyVars } from '../typings'; +import { PackagePolicyVars, SettingsRow } from '../typings'; import { FormRowSetting } from './form_row_setting'; -import { SettingDefinition } from './typings'; import { validateSettingValue } from './utils'; export type FormRowOnChange = (key: string, value: any) => void; @@ -29,73 +28,74 @@ function FormRow({ vars, onChange, }: { - initialSetting: SettingDefinition; + initialSetting: SettingsRow; vars?: PackagePolicyVars; onChange: FormRowOnChange; }) { - function getSettingFormRow(setting: SettingDefinition) { - if (setting.type === 'advanced_settings') { + function getSettingFormRow(row: SettingsRow) { + if (row.type === 'advanced_setting') { return ( - {setting.settings.map((advancedSetting) => + {row.settings.map((advancedSetting) => getSettingFormRow(advancedSetting) )} ); - } else { - const { key } = setting; - const value = vars?.[key]?.value; - const { isValid, message } = validateSettingValue(setting, value); - return ( - - {setting.rowTitle}} - description={setting.rowDescription} - > - {setting.helpText}} - labelAppend={ - - {setting.labelAppend} - - } - > - - - - {setting.settings && - value && - setting.settings.map((childSettings) => - getSettingFormRow(childSettings) - )} - - ); } + + const { key } = row; + const configEntry = vars?.[key]; + // hides a field that doesn't have its key defined in vars. + // This is most likely to happen when a field is no longer supported in the current package version + if (!configEntry) { + return null; + } + const { value } = configEntry; + const { isValid, message } = validateSettingValue(row, value); + return ( + + {row.rowTitle}} + description={row.rowDescription} + > + {row.helpText}} + labelAppend={ + + {row.labelAppend} + + } + > + + + + {row.settings && + value && + row.settings.map((childSettings) => getSettingFormRow(childSettings))} + + ); } return getSettingFormRow(initialSetting); } -interface Props { + +export interface SettingsSection { + id: string; title: string; - subtitle: string; - settings: SettingDefinition[]; + subtitle?: string; + settings: SettingsRow[]; +} + +interface Props { + settingsSection: SettingsSection; vars?: PackagePolicyVars; onChange: FormRowOnChange; } -export function SettingsForm({ - title, - subtitle, - settings, - vars, - onChange, -}: Props) { +export function SettingsForm({ settingsSection, vars, onChange }: Props) { + const { title, subtitle, settings } = settingsSection; return ( @@ -104,11 +104,13 @@ export function SettingsForm({

{title}

- - - {subtitle} - - + {subtitle && ( + + + {subtitle} + + + )}
diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.test.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.test.ts similarity index 73% rename from x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.test.ts rename to x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.test.ts index 3b5cbb652e29..e1be9c547c11 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.test.ts +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.test.ts @@ -5,8 +5,8 @@ * 2.0. */ import { getDurationRt } from '../../../../../common/agent_configuration/runtime_types/duration_rt'; -import { PackagePolicyVars } from '../typings'; -import { SettingDefinition } from './typings'; +import { getIntegerRt } from '../../../../../common/agent_configuration/runtime_types/integer_rt'; +import { PackagePolicyVars, SettingsRow } from '../typings'; import { mergeNewVars, isSettingsFormValid, @@ -16,7 +16,7 @@ import { describe('settings utils', () => { describe('validateSettingValue', () => { it('returns invalid when setting is required and value is empty', () => { - const setting: SettingDefinition = { + const setting: SettingsRow = { key: 'foo', type: 'text', required: true, @@ -27,17 +27,17 @@ describe('settings utils', () => { }); }); it('returns valid when setting is NOT required and value is empty', () => { - const setting: SettingDefinition = { + const setting: SettingsRow = { key: 'foo', type: 'text', }; expect(validateSettingValue(setting, undefined)).toEqual({ isValid: true, - message: '', + message: 'Required field', }); }); it('returns valid when setting does not have a validation property', () => { - const setting: SettingDefinition = { + const setting: SettingsRow = { key: 'foo', type: 'text', }; @@ -46,8 +46,8 @@ describe('settings utils', () => { message: '', }); }); - it('returns valid after validating value', () => { - const setting: SettingDefinition = { + it('returns valid after validating duration value', () => { + const setting: SettingsRow = { key: 'foo', type: 'text', validation: getDurationRt({ min: '1ms' }), @@ -57,8 +57,8 @@ describe('settings utils', () => { message: 'No errors!', }); }); - it('returns invalid after validating value', () => { - const setting: SettingDefinition = { + it('returns invalid after validating duration value', () => { + const setting: SettingsRow = { key: 'foo', type: 'text', validation: getDurationRt({ min: '1ms' }), @@ -68,9 +68,43 @@ describe('settings utils', () => { message: 'Must be greater than 1ms', }); }); + it('returns valid after validating integer value', () => { + const setting: SettingsRow = { + key: 'foo', + type: 'text', + validation: getIntegerRt({ min: 1 }), + }; + expect(validateSettingValue(setting, 1)).toEqual({ + isValid: true, + message: 'No errors!', + }); + }); + it('returns invalid after validating integer value', () => { + const setting: SettingsRow = { + key: 'foo', + type: 'text', + validation: getIntegerRt({ min: 1 }), + }; + expect(validateSettingValue(setting, 0)).toEqual({ + isValid: false, + message: 'Must be greater than 1', + }); + }); + + it('returns valid when required and value is empty', () => { + const setting: SettingsRow = { + key: 'foo', + type: 'text', + required: true, + }; + expect(validateSettingValue(setting, '')).toEqual({ + isValid: false, + message: 'Required field', + }); + }); }); describe('isSettingsFormValid', () => { - const settings: SettingDefinition[] = [ + const settings: SettingsRow[] = [ { key: 'foo', type: 'text', required: true }, { key: 'bar', @@ -79,7 +113,7 @@ describe('settings utils', () => { }, { key: 'baz', type: 'text', validation: getDurationRt({ min: '1ms' }) }, { - type: 'advanced_settings', + type: 'advanced_setting', settings: [ { type: 'text', diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.ts similarity index 80% rename from x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.ts rename to x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.ts index 5f19f297ab33..8f9badabeda5 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings/utils.ts +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/settings_form/utils.ts @@ -8,9 +8,8 @@ import { i18n } from '@kbn/i18n'; import { isRight } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; -import { isEmpty } from 'lodash'; -import { PackagePolicyVars } from '../typings'; -import { SettingDefinition, Setting } from './typings'; +import { isEmpty, isFinite } from 'lodash'; +import { PackagePolicyVars, SettingsRow, BasicSettingRow } from '../typings'; export const REQUIRED_LABEL = i18n.translate( 'xpack.apm.fleet_integration.settings.requiredLabel', @@ -34,13 +33,13 @@ export function mergeNewVars( } export function isSettingsFormValid( - parentSettings: SettingDefinition[], + parentSettings: SettingsRow[], vars: PackagePolicyVars ) { - function isSettingsValid(settings: SettingDefinition[]): boolean { + function isSettingsValid(settings: SettingsRow[]): boolean { return !settings .map((setting) => { - if (setting.type === 'advanced_settings') { + if (setting.type === 'advanced_setting') { return isSettingsValid(setting.settings); } @@ -59,11 +58,11 @@ export function isSettingsFormValid( return isSettingsValid(parentSettings); } -export function validateSettingValue(setting: Setting, value?: any) { - if (isEmpty(value)) { +export function validateSettingValue(setting: BasicSettingRow, value?: any) { + if (!isFinite(value) && isEmpty(value)) { return { isValid: !setting.required, - message: setting.required ? REQUIRED_FIELD : '', + message: REQUIRED_FIELD, }; } diff --git a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/typings.ts b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/typings.ts index 0a5ebde1584c..7df1ccf1fb18 100644 --- a/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/typings.ts +++ b/x-pack/plugins/apm/public/components/fleet_integration/apm_policy_form/typings.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import * as t from 'io-ts'; import { PackagePolicyConfigRecordEntry } from '../../../../../fleet/common'; export { @@ -19,7 +20,33 @@ export { export type PackagePolicyVars = Record; -export type OnFormChangeFn = ( - newVars: PackagePolicyVars, - isValid: boolean -) => void; +export type SettingValidation = t.Type; + +interface AdvancedSettingRow { + type: 'advanced_setting'; + settings: SettingsRow[]; +} + +export interface BasicSettingRow { + type: + | 'text' + | 'combo' + | 'area' + | 'boolean' + | 'integer' + | 'bytes' + | 'duration'; + key: string; + rowTitle?: string; + rowDescription?: string; + label?: string; + helpText?: string; + placeholder?: string; + labelAppend?: string; + settings?: SettingsRow[]; + validation?: SettingValidation; + required?: boolean; + readOnly?: boolean; +} + +export type SettingsRow = BasicSettingRow | AdvancedSettingRow; diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx index d38d51f01c67..cfb51b4cd3b6 100644 --- a/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx @@ -72,7 +72,7 @@ function getFleetLink({ } : { label: GET_STARTED_WITH_FLEET_LABEL, - href: `${basePath}/app/integrations#/detail/apm-0.3.0/overview`, + href: `${basePath}/app/integrations#/detail/apm-0.4.0/overview`, }; } diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx index 2ea73126711a..fbbb2e1ffedf 100644 --- a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx @@ -93,7 +93,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { {i18n.translate( 'xpack.apm.tutorial.apmServer.fleet.apmIntegration.button', diff --git a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts index 0b086d1998be..b339c1f1f0be 100644 --- a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts +++ b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts @@ -32,7 +32,7 @@ export function getApmPackagePolicyDefinition( ], package: { name: APM_PACKAGE_NAME, - version: '0.3.0', + version: '0.4.0', title: 'Elastic APM', }, }; @@ -74,14 +74,6 @@ export const apmConfigMapping: Record< type: 'text', getValue: ({ cloudPluginSetup }) => cloudPluginSetup?.apm?.url, }, - 'apm-server.secret_token': { - name: 'secret_token', - type: 'text', - }, - 'apm-server.api_key.enabled': { - name: 'api_key_enabled', - type: 'bool', - }, 'apm-server.rum.enabled': { name: 'enable_rum', type: 'bool', @@ -90,10 +82,6 @@ export const apmConfigMapping: Record< name: 'default_service_environment', type: 'text', }, - 'apm-server.rum.allow_service_names': { - name: 'rum_allow_service_names', - type: 'text', - }, 'apm-server.rum.allow_origins': { name: 'rum_allow_origins', type: 'text', @@ -106,14 +94,6 @@ export const apmConfigMapping: Record< name: 'rum_response_headers', type: 'yaml', }, - 'apm-server.rum.event_rate.limit': { - name: 'rum_event_rate_limit', - type: 'integer', - }, - 'apm-server.rum.event_rate.lru_size': { - name: 'rum_event_rate_lru_size', - type: 'integer', - }, 'apm-server.rum.library_pattern': { name: 'rum_library_pattern', type: 'text', @@ -122,10 +102,6 @@ export const apmConfigMapping: Record< name: 'rum_exclude_from_grouping', type: 'text', }, - 'apm-server.api_key.limit': { - name: 'api_key_limit', - type: 'integer', - }, 'apm-server.max_event_size': { name: 'max_event_bytes', type: 'integer', @@ -190,4 +166,36 @@ export const apmConfigMapping: Record< name: 'tls_curve_types', type: 'text', }, + 'apm-server.auth.secret_token': { + name: 'secret_token', + type: 'text', + }, + 'apm-server.auth.api_key.enabled': { + name: 'api_key_enabled', + type: 'bool', + }, + 'apm-server.auth.api_key.limit': { + name: 'api_key_limit', + type: 'bool', + }, + 'apm-server.auth.anonymous.enabled': { + name: 'anonymous_enabled', + type: 'bool', + }, + 'apm-server.auth.anonymous.allow_agent': { + name: 'anonymous_allow_agent', + type: 'text', + }, + 'apm-server.auth.anonymous.allow_service': { + name: 'anonymous_allow_service', + type: 'text', + }, + 'apm-server.auth.anonymous.rate_limit.ip_limit': { + name: 'anonymous_rate_limit_ip_limit', + type: 'integer', + }, + 'apm-server.auth.anonymous.rate_limit.event_limit': { + name: 'anonymous_rate_limit_event_limit', + type: 'integer', + }, };