mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Response Ops][Connectors] New xpack.actions.webhook.ssl.pfx.enabled
config (#222507)
## Summary Closes https://github.com/elastic/kibana/issues/220416 ## Release note New `xpack.actions.webhook.ssl.pfx.enabled` Kibana setting to disable Webhook connector PFX file support for SSL client authentication --------- Co-authored-by: Nastasha Solomon <79124755+nastasha-solomon@users.noreply.github.com>
This commit is contained in:
parent
dfd783e12a
commit
25b4f507e2
28 changed files with 396 additions and 23 deletions
|
@ -200,6 +200,7 @@ enabled:
|
|||
- x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/config.ts
|
||||
- x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/with_aws_ses_kibana_config/config.ts
|
||||
- x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/shared/config.ts
|
||||
- x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/webhook_disabled_ssl_pfx/config.ts
|
||||
- x-pack/test/functional/apps/advanced_settings/config.ts
|
||||
- x-pack/test/functional/apps/aiops/config.ts
|
||||
- x-pack/test/functional/apps/api_keys/config.ts
|
||||
|
|
|
@ -841,6 +841,12 @@ For more examples, go to [Preconfigured connectors](/reference/connectors-kibana
|
|||
|
||||
Data type: `string`
|
||||
|
||||
`xpack.actions.webhook.ssl.pfx.enabled`
|
||||
: Disable PFX file support for SSL client authentication. When set to `false`, the application will not accept PFX certificate files and will require separate certificate and private key files instead. Only applies to the [Webhook connector](/reference/connectors-kibana/webhook-action-type.md).
|
||||
|
||||
Data type: `bool`
|
||||
Default: `true`
|
||||
|
||||
## Alerting settings [alert-settings]
|
||||
|
||||
`xpack.alerting.cancelAlertsOnRuleTimeout` 
|
||||
|
|
|
@ -2303,6 +2303,27 @@ groups:
|
|||
self: all
|
||||
# example: |
|
||||
|
||||
- setting: xpack.actions.webhook.ssl.pfx.enabled
|
||||
# id:
|
||||
description: |
|
||||
Disable PFX file support for SSL client authentication. When set to `false`, the application will not accept PFX certificate files and will require separate certificate and private key files instead. Only applies to the [Webhook connector](/reference/connectors-kibana/webhook-action-type.md).
|
||||
# state: deprecated/hidden/tech-preview
|
||||
# deprecation_details: ""
|
||||
# note: ""
|
||||
# tip: ""
|
||||
# warning: ""
|
||||
# important: ""
|
||||
datatype: bool
|
||||
default: true
|
||||
# options:
|
||||
# - option:
|
||||
# description: ""
|
||||
# type: static/dynamic
|
||||
applies_to:
|
||||
deployment:
|
||||
self: all
|
||||
# example: |
|
||||
|
||||
- group: Alerting settings
|
||||
id: alert-settings
|
||||
# description: |
|
||||
|
|
|
@ -226,6 +226,7 @@ kibana_vars=(
|
|||
xpack.actions.responseTimeout
|
||||
xpack.actions.ssl.proxyVerificationMode
|
||||
xpack.actions.ssl.verificationMode
|
||||
xpack.actions.webhook.ssl.pfx.enabled
|
||||
xpack.alerting.healthCheck.interval
|
||||
xpack.alerting.invalidateApiKeysTask.interval
|
||||
xpack.alerting.invalidateApiKeysTask.removalDelay
|
||||
|
|
|
@ -203,6 +203,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'vis_type_xy.readOnly (boolean?|never)',
|
||||
'vis_type_vega.enableExternalUrls (boolean?)',
|
||||
'xpack.actions.email.domain_allowlist (array?)',
|
||||
'xpack.actions.webhook.ssl.pfx.enabled (boolean?)',
|
||||
'xpack.apm.serviceMapEnabled (boolean?)',
|
||||
'xpack.apm.ui.enabled (boolean?)',
|
||||
'xpack.apm.ui.maxTraceItems (number?)',
|
||||
|
|
|
@ -62,5 +62,20 @@ describe('Actions Plugin', () => {
|
|||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns isWebhookSslWithPfxEnabled if set in kibana config', async () => {
|
||||
const context = coreMock.createPluginInitializerContext({
|
||||
webhook: {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const plugin = new Plugin(context);
|
||||
const pluginSetup = plugin.setup();
|
||||
expect(pluginSetup.isWebhookSslWithPfxEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,26 +14,37 @@ export interface ActionsPublicPluginSetup {
|
|||
emails: string[],
|
||||
options?: ValidateEmailAddressesOptions
|
||||
): ValidatedEmail[];
|
||||
isWebhookSslWithPfxEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
email: {
|
||||
domain_allowlist: string[];
|
||||
};
|
||||
webhook: {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export class Plugin implements CorePlugin<ActionsPublicPluginSetup> {
|
||||
private readonly allowedEmailDomains: string[] | null = null;
|
||||
private readonly webhookSslWithPfxEnabled: boolean;
|
||||
|
||||
constructor(ctx: PluginInitializerContext<Config>) {
|
||||
const config = ctx.config.get();
|
||||
this.allowedEmailDomains = config.email?.domain_allowlist || null;
|
||||
this.webhookSslWithPfxEnabled = config.webhook?.ssl.pfx.enabled ?? true;
|
||||
}
|
||||
|
||||
public setup(): ActionsPublicPluginSetup {
|
||||
return {
|
||||
validateEmailAddresses: (emails: string[], options: ValidateEmailAddressesOptions) =>
|
||||
validateEmails(this.allowedEmailDomains, emails, options),
|
||||
isWebhookSslWithPfxEnabled: this.webhookSslWithPfxEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,13 @@ const createActionsConfigMock = () => {
|
|||
getMaxAttempts: jest.fn().mockReturnValue(3),
|
||||
enableFooterInEmail: jest.fn().mockReturnValue(true),
|
||||
getMaxQueued: jest.fn().mockReturnValue(1000),
|
||||
getWebhookSettings: jest.fn().mockReturnValue({
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
getAwsSesConfig: jest.fn().mockReturnValue(null),
|
||||
};
|
||||
return mocked;
|
||||
|
|
|
@ -572,6 +572,56 @@ describe('getMaxQueued()', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getWebhookSettings()', () => {
|
||||
test('returns the webhook settings from config', () => {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
webhook: {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const webhookSettings = getActionsConfigurationUtilities(config).getWebhookSettings();
|
||||
expect(webhookSettings).toEqual({
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns the webhook settings from config when pfx is false', () => {
|
||||
const config: ActionsConfig = {
|
||||
...defaultActionsConfig,
|
||||
webhook: {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const webhookSettings = getActionsConfigurationUtilities(config).getWebhookSettings();
|
||||
expect(webhookSettings).toEqual({
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('returns true when no webhook settings are defined', () => {
|
||||
const config: ActionsConfig = defaultActionsConfig;
|
||||
const webhookSettings = getActionsConfigurationUtilities(config).getWebhookSettings();
|
||||
expect(webhookSettings).toEqual({ ssl: { pfx: { enabled: true } } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAwsSesConfig()', () => {
|
||||
test('returns null when no email config set', () => {
|
||||
const acu = getActionsConfigurationUtilities(defaultActionsConfig);
|
||||
|
|
|
@ -55,6 +55,13 @@ export interface ActionsConfigurationUtilities {
|
|||
): string | undefined;
|
||||
enableFooterInEmail: () => boolean;
|
||||
getMaxQueued: () => number;
|
||||
getWebhookSettings(): {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
getAwsSesConfig: () => AwsSesConfig;
|
||||
}
|
||||
|
||||
|
@ -226,6 +233,15 @@ export function getActionsConfigurationUtilities(
|
|||
},
|
||||
enableFooterInEmail: () => config.enableFooterInEmail,
|
||||
getMaxQueued: () => config.queued?.max || DEFAULT_QUEUED_MAX,
|
||||
getWebhookSettings: () => {
|
||||
return {
|
||||
ssl: {
|
||||
pfx: {
|
||||
enabled: config.webhook?.ssl.pfx.enabled ?? true,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
getAwsSesConfig: () => {
|
||||
if (config.email?.services?.ses.host && config.email?.services?.ses.port) {
|
||||
return {
|
||||
|
|
|
@ -250,6 +250,32 @@ describe('config validation', () => {
|
|||
expect(result.email?.domain_allowlist).toEqual(['a.com', 'b.c.com', 'd.e.f.com']);
|
||||
});
|
||||
|
||||
test('validates xpack.actions.webhook', () => {
|
||||
const config: Record<string, unknown> = {};
|
||||
let result = configSchema.validate(config);
|
||||
expect(result.webhook === undefined);
|
||||
|
||||
config.webhook = {};
|
||||
result = configSchema.validate(config);
|
||||
expect(result.webhook?.ssl.pfx.enabled).toEqual(true);
|
||||
|
||||
config.webhook = { ssl: {} };
|
||||
result = configSchema.validate(config);
|
||||
expect(result.webhook?.ssl.pfx.enabled).toEqual(true);
|
||||
|
||||
config.webhook = { ssl: { pfx: {} } };
|
||||
result = configSchema.validate(config);
|
||||
expect(result.webhook?.ssl.pfx.enabled).toEqual(true);
|
||||
|
||||
config.webhook = { ssl: { pfx: { enabled: false } } };
|
||||
result = configSchema.validate(config);
|
||||
expect(result.webhook?.ssl.pfx.enabled).toEqual(false);
|
||||
|
||||
config.webhook = { ssl: { pfx: { enabled: true } } };
|
||||
result = configSchema.validate(config);
|
||||
expect(result.webhook?.ssl.pfx.enabled).toEqual(true);
|
||||
});
|
||||
|
||||
describe('email.services.ses', () => {
|
||||
const config: Record<string, unknown> = {};
|
||||
test('validates no email config at all', () => {
|
||||
|
|
|
@ -167,6 +167,15 @@ export const configSchema = schema.object({
|
|||
})
|
||||
),
|
||||
}),
|
||||
webhook: schema.maybe(
|
||||
schema.object({
|
||||
ssl: schema.object({
|
||||
pfx: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type ActionsConfig = TypeOf<typeof configSchema>;
|
||||
|
|
|
@ -51,6 +51,7 @@ export const config: PluginConfigDescriptor<ActionsConfig> = {
|
|||
schema: configSchema,
|
||||
exposeToBrowser: {
|
||||
email: { domain_allowlist: true },
|
||||
webhook: { ssl: { pfx: { enabled: true } } },
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -39,13 +39,14 @@ import * as i18n from './translations';
|
|||
|
||||
interface Props {
|
||||
readOnly: boolean;
|
||||
isPfxEnabled?: boolean;
|
||||
}
|
||||
|
||||
const { emptyField } = fieldValidators;
|
||||
|
||||
const VERIFICATION_MODE_DEFAULT = 'full';
|
||||
|
||||
export const AuthConfig: FunctionComponent<Props> = ({ readOnly }) => {
|
||||
export const AuthConfig: FunctionComponent<Props> = ({ readOnly, isPfxEnabled = true }) => {
|
||||
const { setFieldValue, getFieldDefaultValue } = useFormContext();
|
||||
const [{ config, __internal__ }] = useFormData({
|
||||
watch: [
|
||||
|
@ -112,6 +113,7 @@ export const AuthConfig: FunctionComponent<Props> = ({ readOnly }) => {
|
|||
readOnly={readOnly}
|
||||
certTypeDefaultValue={certTypeDefaultValue}
|
||||
certType={certType}
|
||||
isPfxEnabled={isPfxEnabled}
|
||||
/>
|
||||
),
|
||||
'data-test-subj': 'authSSL',
|
||||
|
@ -180,7 +182,7 @@ export const AuthConfig: FunctionComponent<Props> = ({ readOnly }) => {
|
|||
onClick={() => removeItem(item.id)}
|
||||
iconType="minusInCircle"
|
||||
aria-label={i18n.DELETE_BUTTON}
|
||||
style={{ marginTop: '28px' }}
|
||||
css={{ marginTop: '28px' }}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -11,10 +11,26 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { SSLCertType } from '../../../common/auth/constants';
|
||||
import { AuthFormTestProvider } from '../../connector_types/lib/test_utils';
|
||||
import { useConnectorContext } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const certTypeDefaultValue: SSLCertType = SSLCertType.CRT;
|
||||
|
||||
jest.mock('@kbn/triggers-actions-ui-plugin/public', () => ({
|
||||
useConnectorContext: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('SSLCertFields', () => {
|
||||
beforeEach(() => {
|
||||
(useConnectorContext as jest.Mock).mockReturnValue({
|
||||
services: { isWebhookSslWithPfxEnabled: true },
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const onSubmit = jest.fn();
|
||||
|
||||
it('renders all fields for certType=CRT', async () => {
|
||||
|
@ -221,3 +237,33 @@ describe('SSLCertFields', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validation with PFX disabled', () => {
|
||||
beforeEach(() => {
|
||||
(useConnectorContext as jest.Mock).mockReturnValue({
|
||||
services: { isWebhookSslWithPfxEnabled: false },
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('does not render PFX tab when PFX is disabled', async () => {
|
||||
render(
|
||||
<AuthFormTestProvider onSubmit={jest.fn()}>
|
||||
<SSLCertFields
|
||||
readOnly={false}
|
||||
certTypeDefaultValue={certTypeDefaultValue}
|
||||
certType={SSLCertType.CRT}
|
||||
isPfxEnabled={false}
|
||||
/>
|
||||
</AuthFormTestProvider>
|
||||
);
|
||||
|
||||
expect(await screen.findByTestId('sslCertFields')).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('webhookSSLPassphraseInput')).toBeInTheDocument();
|
||||
expect(await screen.findByTestId('webhookCertTypeTabs')).toBeInTheDocument();
|
||||
expect(screen.queryByText(i18n.CERT_TYPE_PFX)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,12 +22,14 @@ interface BasicAuthProps {
|
|||
readOnly: boolean;
|
||||
certTypeDefaultValue: SSLCertType;
|
||||
certType: SSLCertType;
|
||||
isPfxEnabled?: boolean;
|
||||
}
|
||||
|
||||
export const SSLCertFields: React.FC<BasicAuthProps> = ({
|
||||
readOnly,
|
||||
certTypeDefaultValue,
|
||||
certType,
|
||||
isPfxEnabled = true,
|
||||
}) => (
|
||||
<EuiFlexGroup justifyContent="spaceBetween" data-test-subj="sslCertFields">
|
||||
<EuiFlexItem>
|
||||
|
@ -52,21 +54,25 @@ export const SSLCertFields: React.FC<BasicAuthProps> = ({
|
|||
<EuiTabs size="s" data-test-subj="webhookCertTypeTabs">
|
||||
<EuiTab
|
||||
onClick={() => field.setValue(SSLCertType.CRT)}
|
||||
isSelected={field.value === SSLCertType.CRT}
|
||||
isSelected={field.value === SSLCertType.CRT || !isPfxEnabled}
|
||||
data-test-subj="webhookCertTypeCRTab"
|
||||
>
|
||||
{i18n.CERT_TYPE_CRT_KEY}
|
||||
</EuiTab>
|
||||
{isPfxEnabled && (
|
||||
<EuiTab
|
||||
onClick={() => field.setValue(SSLCertType.PFX)}
|
||||
isSelected={field.value === SSLCertType.PFX}
|
||||
data-test-subj="webhookCertTypePFXTab"
|
||||
>
|
||||
{i18n.CERT_TYPE_PFX}
|
||||
</EuiTab>
|
||||
)}
|
||||
</EuiTabs>
|
||||
)}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
{certType === SSLCertType.CRT && (
|
||||
{(!isPfxEnabled || certType === SSLCertType.CRT) && (
|
||||
<EuiFlexGroup wrap>
|
||||
<EuiFlexItem css={{ minWidth: 200 }}>
|
||||
<UseField
|
||||
|
@ -112,7 +118,7 @@ export const SSLCertFields: React.FC<BasicAuthProps> = ({
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
{certType === SSLCertType.PFX && (
|
||||
{isPfxEnabled && certType === SSLCertType.PFX && (
|
||||
<UseField
|
||||
path="secrets.pfx"
|
||||
config={{
|
||||
|
|
|
@ -177,7 +177,7 @@ const CasesWebhookActionConnectorFields: React.FunctionComponent<ActionConnector
|
|||
<UpdateStep readOnly={readOnly} display={currentStep === 4} />
|
||||
<EuiFlexGroup alignItems="flexStart" justifyContent="flexStart" direction="rowReverse">
|
||||
{currentStep < 4 && (
|
||||
<EuiFlexItem grow={false} style={{ minWidth: 160 }}>
|
||||
<EuiFlexItem grow={false} css={{ minWidth: 160 }}>
|
||||
<EuiButton
|
||||
data-test-subj="casesWebhookNext"
|
||||
fill
|
||||
|
@ -190,7 +190,7 @@ const CasesWebhookActionConnectorFields: React.FunctionComponent<ActionConnector
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
{currentStep > 1 && (
|
||||
<EuiFlexItem grow={false} style={{ minWidth: 160 }}>
|
||||
<EuiFlexItem grow={false} css={{ minWidth: 160 }}>
|
||||
<EuiButton
|
||||
data-test-subj="casesWebhookBack"
|
||||
iconSide="left"
|
||||
|
|
|
@ -71,7 +71,10 @@ const FormTestProviderComponent: React.FC<FormTestProviderProps> = ({
|
|||
children,
|
||||
defaultValue,
|
||||
onSubmit,
|
||||
connectorServices = { validateEmailAddresses: jest.fn() },
|
||||
connectorServices = {
|
||||
validateEmailAddresses: jest.fn(),
|
||||
isWebhookSslWithPfxEnabled: true,
|
||||
},
|
||||
}) => {
|
||||
const { form } = useForm({ defaultValue });
|
||||
const { submit } = form;
|
||||
|
|
|
@ -11,7 +11,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
|||
import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
|
||||
import { Field, SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components';
|
||||
import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
|
||||
import type { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import {
|
||||
useConnectorContext,
|
||||
type ActionConnectorFieldsProps,
|
||||
} from '@kbn/triggers-actions-ui-plugin/public';
|
||||
|
||||
import * as i18n from './translations';
|
||||
import { AuthConfig } from '../../common/auth/auth_config';
|
||||
|
@ -22,6 +25,10 @@ const { emptyField, urlField } = fieldValidators;
|
|||
const WebhookActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps> = ({
|
||||
readOnly,
|
||||
}) => {
|
||||
const {
|
||||
services: { isWebhookSslWithPfxEnabled: isPfxEnabled },
|
||||
} = useConnectorContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
|
@ -67,7 +74,7 @@ const WebhookActionConnectorFields: React.FunctionComponent<ActionConnectorField
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
<AuthConfig readOnly={readOnly} />
|
||||
<AuthConfig readOnly={readOnly} isPfxEnabled={isPfxEnabled} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -222,7 +222,7 @@ describe('config validation', () => {
|
|||
expect(() => {
|
||||
validateConfig(connectorType, config, { configurationUtilities });
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
'"error validating action type config: error configuring webhook action: unable to parse url: TypeError: Invalid URL: example.com/do-something"'
|
||||
`"error validating action type config: error validation webhook action config: unable to parse url: TypeError: Invalid URL: example.com/do-something"`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -295,7 +295,25 @@ describe('config validation', () => {
|
|||
expect(() => {
|
||||
validateConfig(connectorType, config, { configurationUtilities: configUtils });
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"error validating action type config: error configuring webhook action: target url is not present in allowedHosts"`
|
||||
`"error validating action type config: error validation webhook action config: target url is not present in allowedHosts"`
|
||||
);
|
||||
});
|
||||
|
||||
test('config validation fails when using disabled pfx certType', () => {
|
||||
const config: Record<string, string | boolean> = {
|
||||
url: 'https://mylisteningserver:9200/endpoint',
|
||||
method: WebhookMethods.POST,
|
||||
authType: AuthType.SSL,
|
||||
certType: SSLCertType.PFX,
|
||||
hasAuth: true,
|
||||
};
|
||||
configurationUtilities.getWebhookSettings = jest.fn(() => ({
|
||||
ssl: { pfx: { enabled: false } },
|
||||
}));
|
||||
expect(() => {
|
||||
validateConfig(connectorType, config, { configurationUtilities });
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"error validating action type config: error validation webhook action config: certType \\"ssl-pfx\\" is disabled"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
import { renderMustacheString } from '@kbn/actions-plugin/server/lib/mustache_renderer';
|
||||
import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib';
|
||||
|
||||
import { SSLCertType } from '../../../common/auth/constants';
|
||||
import type {
|
||||
WebhookConnectorType,
|
||||
ActionParamsType,
|
||||
|
@ -95,7 +96,7 @@ function validateConnectorTypeConfig(
|
|||
} catch (err) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.stackConnectors.webhook.configurationErrorNoHostname', {
|
||||
defaultMessage: 'error configuring webhook action: unable to parse url: {err}',
|
||||
defaultMessage: 'error validation webhook action config: unable to parse url: {err}',
|
||||
values: {
|
||||
err: err.toString(),
|
||||
},
|
||||
|
@ -108,7 +109,7 @@ function validateConnectorTypeConfig(
|
|||
} catch (allowListError) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.stackConnectors.webhook.configurationError', {
|
||||
defaultMessage: 'error configuring webhook action: {message}',
|
||||
defaultMessage: 'error validation webhook action config: {message}',
|
||||
values: {
|
||||
message: allowListError.message,
|
||||
},
|
||||
|
@ -120,10 +121,25 @@ function validateConnectorTypeConfig(
|
|||
throw new Error(
|
||||
i18n.translate('xpack.stackConnectors.webhook.authConfigurationError', {
|
||||
defaultMessage:
|
||||
'error configuring webhook action: authType must be null or undefined if hasAuth is false',
|
||||
'error validation webhook action config: authType must be null or undefined if hasAuth is false',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (configObject.certType === SSLCertType.PFX) {
|
||||
const webhookSettings = configurationUtilities.getWebhookSettings();
|
||||
if (!webhookSettings.ssl.pfx.enabled) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.stackConnectors.webhook.pfxConfigurationError', {
|
||||
defaultMessage:
|
||||
'error validation webhook action config: certType "{certType}" is disabled',
|
||||
values: {
|
||||
certType: SSLCertType.PFX,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// action executor
|
||||
|
|
|
@ -90,11 +90,11 @@ export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
|
|||
|
||||
export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) => {
|
||||
const {
|
||||
actions: { validateEmailAddresses },
|
||||
actions: { validateEmailAddresses, isWebhookSslWithPfxEnabled },
|
||||
} = useKibana().services;
|
||||
|
||||
return (
|
||||
<ConnectorProvider value={{ services: { validateEmailAddresses } }}>
|
||||
<ConnectorProvider value={{ services: { validateEmailAddresses, isWebhookSslWithPfxEnabled } }}>
|
||||
<Routes>
|
||||
<Route
|
||||
path={`/:section(${sectionsRegex})`}
|
||||
|
|
|
@ -400,6 +400,7 @@ export interface SnoozeSchedule {
|
|||
|
||||
export interface ConnectorServices {
|
||||
validateEmailAddresses: ActionsPublicPluginSetup['validateEmailAddresses'];
|
||||
isWebhookSslWithPfxEnabled?: ActionsPublicPluginSetup['isWebhookSslWithPfxEnabled'];
|
||||
}
|
||||
|
||||
export interface RulesListFilters {
|
||||
|
|
|
@ -13,5 +13,6 @@ export default ({ loadTestFile }: FtrProviderContext) => {
|
|||
loadTestFile(require.resolve('./opsgenie'));
|
||||
loadTestFile(require.resolve('./tines'));
|
||||
loadTestFile(require.resolve('./slack'));
|
||||
loadTestFile(require.resolve('./webhook'));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const find = getService('find');
|
||||
const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']);
|
||||
|
||||
describe('webhook', () => {
|
||||
beforeEach(async () => {
|
||||
await pageObjects.common.navigateToApp('triggersActionsConnectors');
|
||||
});
|
||||
|
||||
it('should render the cr and pfx tab for ssl auth', async () => {
|
||||
await pageObjects.triggersActionsUI.clickCreateConnectorButton();
|
||||
await testSubjects.click('.webhook-card');
|
||||
await testSubjects.click('authSSL');
|
||||
|
||||
const certTypeTabs = await find.allByCssSelector(
|
||||
'[data-test-subj="webhookCertTypeTabs"] > .euiTab'
|
||||
);
|
||||
expect(certTypeTabs.length).to.be(2);
|
||||
expect(await certTypeTabs[0].getAttribute('data-test-subj')).to.be('webhookCertTypeCRTab');
|
||||
expect(await certTypeTabs[1].getAttribute('data-test-subj')).to.be('webhookCertTypePFXTab');
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const baseConfig = await readConfigFile(require.resolve('../../../../config.base.ts'));
|
||||
|
||||
return {
|
||||
...baseConfig.getAll(),
|
||||
testFiles: [require.resolve('.')],
|
||||
junit: {
|
||||
reportName: 'Chrome X-Pack UI Functional Tests with ES SSL - Disabled Webhook SSL PFX',
|
||||
},
|
||||
kbnTestServer: {
|
||||
...baseConfig.getAll().kbnTestServer,
|
||||
serverArgs: [
|
||||
...baseConfig.getAll().kbnTestServer.serverArgs,
|
||||
`--xpack.actions.webhook.ssl.pfx.enabled=false`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default ({ loadTestFile }: FtrProviderContext) => {
|
||||
describe('Webhook - disabled ssl pfx', function () {
|
||||
loadTestFile(require.resolve('./webhook'));
|
||||
});
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const find = getService('find');
|
||||
const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']);
|
||||
|
||||
describe('webhook', () => {
|
||||
beforeEach(async () => {
|
||||
await pageObjects.common.navigateToApp('triggersActionsConnectors');
|
||||
});
|
||||
|
||||
it('should not render the pfx tab for ssl auth', async () => {
|
||||
await pageObjects.triggersActionsUI.clickCreateConnectorButton();
|
||||
await testSubjects.click('.webhook-card');
|
||||
await testSubjects.click('authSSL');
|
||||
|
||||
const certTypeTabs = await find.allByCssSelector(
|
||||
'[data-test-subj="webhookCertTypeTabs"] > .euiTab'
|
||||
);
|
||||
expect(certTypeTabs.length).to.be(1);
|
||||
expect(await certTypeTabs[0].getAttribute('data-test-subj')).to.be('webhookCertTypeCRTab');
|
||||
});
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue