[Response Ops] [Connectors] Move connectors to own plugin (#139867)

* Initial commit of stack connectors plugin

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Fixing up xmatters. Moving library functions around

* [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs'

* Fixing up webhook

* Fixing up teams and slack

* Fixing up index, email, pagerduty, server log

* Fixing i18n

* wip

* Moving well know email route to stack connectors plugin

* Fixing types

* Fixing unit tests

* Adding index.ts

* Cleanup

* Updating READMEs

* [CI] Auto-commit changed files from 'node scripts/build_plugin_list_docs'

* Moving to domain specific folders and updating codeowners

* Fixing codeowners

* Fixing types

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Ying Mao 2022-09-15 15:50:15 -04:00 committed by GitHub
parent 2464aa69b7
commit 914d1cd41c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
189 changed files with 2562 additions and 2315 deletions

View file

@ -1313,7 +1313,7 @@ module.exports = {
{
// typescript for front and back end
files: [
'x-pack/plugins/{alerting,stack_alerts,actions,task_manager,event_log}/**/*.{ts,tsx}',
'x-pack/plugins/{alerting,stack_alerts,stack_connectors,actions,task_manager,event_log}/**/*.{ts,tsx}',
],
rules: {
'@typescript-eslint/no-explicit-any': 'error',

3
.github/CODEOWNERS vendored
View file

@ -326,6 +326,9 @@ x-pack/examples/files_example @elastic/kibana-app-services
/x-pack/plugins/actions/ @elastic/response-ops
/x-pack/plugins/event_log/ @elastic/response-ops
/x-pack/plugins/task_manager/ @elastic/response-ops
/x-pack/plugins/stack_connectors/ @elastic/response-ops
/x-pack/plugins/stack_connectors/server/connector_types/stack/ @elastic/response-ops-execution
/x-pack/plugins/stack_connectors/server/connector_types/cases/ @elastic/response-ops-cases
/x-pack/test/alerting_api_integration/ @elastic/response-ops
/x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/response-ops
/x-pack/plugins/triggers_actions_ui/ @elastic/response-ops

View file

@ -645,6 +645,10 @@ the alertTypes by the Stack in the alerting plugin, register associated HTTP
routes, etc.
|{kib-repo}blob/{branch}/x-pack/plugins/stack_connectors/README.md[stackConnectors]
|The stack_connectors plugin provides connector types shipped with Kibana, built on top of the framework provided in the actions plugin.
|{kib-repo}blob/{branch}/x-pack/plugins/synthetics/README.md[synthetics]
|The purpose of this plugin is to provide users of Heartbeat more visibility of what's happening
in their infrastructure.

View file

@ -419,6 +419,8 @@
"@kbn/spaces-plugin/*": ["x-pack/plugins/spaces/*"],
"@kbn/stack-alerts-plugin": ["x-pack/plugins/stack_alerts"],
"@kbn/stack-alerts-plugin/*": ["x-pack/plugins/stack_alerts/*"],
"@kbn/stack-connectors-plugin": ["x-pack/plugins/stack_connectors"],
"@kbn/stack-connectors-plugin/*": ["x-pack/plugins/stack_connectors/*"],
"@kbn/synthetics-plugin": ["x-pack/plugins/synthetics"],
"@kbn/synthetics-plugin/*": ["x-pack/plugins/synthetics/*"],
"@kbn/task-manager-plugin": ["x-pack/plugins/task_manager"],

View file

@ -5,6 +5,7 @@
"xpack.alerting": "plugins/alerting",
"xpack.eventLog": "plugins/event_log",
"xpack.stackAlerts": "plugins/stack_alerts",
"xpack.stackConnectors": "plugins/stack_connectors",
"xpack.apm": "plugins/apm",
"xpack.canvas": "plugins/canvas",
"xpack.cases": "plugins/cases",

View file

@ -32,49 +32,7 @@ Table of Contents
- [Example](#example-1)
- [actionsClient.execute(options)](#actionsclientexecuteoptions)
- [Example](#example-2)
- [Built-in Action Types](#built-in-action-types)
- [ServiceNow ITSM](#servicenow-itsm)
- [`params`](#params)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice)
- [`subActionParams (getFields)`](#subactionparams-getfields)
- [`subActionParams (getIncident)`](#subactionparams-getincident)
- [`subActionParams (getChoices)`](#subactionparams-getchoices)
- [ServiceNow Sec Ops](#servicenow-sec-ops)
- [`params`](#params-1)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1)
- [`subActionParams (getFields)`](#subactionparams-getfields-1)
- [`subActionParams (getIncident)`](#subactionparams-getincident-1)
- [`subActionParams (getChoices)`](#subactionparams-getchoices-1)
- [ServiceNow ITOM](#servicenow-itom)
- [`params`](#params-2)
- [`subActionParams (addEvent)`](#subactionparams-addevent)
- [`subActionParams (getChoices)`](#subactionparams-getchoices-2)
- [Jira](#jira)
- [`params`](#params-3)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2)
- [`subActionParams (getIncident)`](#subactionparams-getincident-2)
- [`subActionParams (issueTypes)`](#subactionparams-issuetypes)
- [`subActionParams (fieldsByIssueType)`](#subactionparams-fieldsbyissuetype)
- [`subActionParams (issues)`](#subactionparams-issues)
- [`subActionParams (issue)`](#subactionparams-issue)
- [`subActionParams (getFields)`](#subactionparams-getfields-2)
- [IBM Resilient](#ibm-resilient)
- [`params`](#params-4)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3)
- [`subActionParams (getFields)`](#subactionparams-getfields-3)
- [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes)
- [`subActionParams (severity)`](#subactionparams-severity)
- [Swimlane](#swimlane)
- [`params`](#params-5)
- [| severity | The severity of the incident. | string _(optional)_ |](#-severity-----the-severity-of-the-incident-----string-optional-)
- [Command Line Utility](#command-line-utility)
- [Developing New Action Types](#developing-new-action-types)
- [licensing](#licensing)
- [plugin location](#plugin-location)
- [documentation](#documentation)
- [tests](#tests)
- [action type config and secrets](#action-type-config-and-secrets)
- [user interface](#user-interface)
## Terminology
@ -251,292 +209,6 @@ const result = await actionsClient.execute({
}),
});
```
# Built-in Action Types
Kibana ships with a set of built-in action types. See [Actions and connector types Documentation](https://www.elastic.co/guide/en/kibana/master/action-types.html).
In addition to the documented configurations, several built in action type offer additional `params` configurations.
## ServiceNow ITSM
Refer to the [Run connector API documentation](https://www.elastic.co/guide/en/kibana/master/execute-connector-api.html#execute-connector-api-request-body)
for the full list of properties.
### `params`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `getIncident`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The ServiceNow incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------- |
| short_description | The title of the incident. | string |
| description | The description of the incident. | string _(optional)_ |
| externalId | The ID of the incident in ServiceNow. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| severity | The severity in ServiceNow. | string _(optional)_ |
| urgency | The urgency in ServiceNow. | string _(optional)_ |
| impact | The impact in ServiceNow. | string _(optional)_ |
| category | The category in ServiceNow. | string _(optional)_ |
| subcategory | The subcategory in ServiceNow. | string _(optional)_ |
| correlation_id | The correlation id of the incident. | string _(optional)_ |
| correlation_display | The correlation display of the ServiceNow. | string _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ------------------------------------- | ------ |
| externalId | The ID of the incident in ServiceNow. | string |
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | -------------------------------------------------- | -------- |
| fields | An array of fields. Example: `[category, impact]`. | string[] |
---
## ServiceNow Sec Ops
Refer to the [Run connector API documentation](https://www.elastic.co/guide/en/kibana/master/execute-connector-api.html#execute-connector-api-request-body)
for the full list of properties.
### `params`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `getIncident`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The ServiceNow security incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| short_description | The title of the security incident. | string |
| description | The description of the security incident. | string _(optional)_ |
| externalId | The ID of the security incident in ServiceNow. If present, the security incident is updated. Otherwise, a new security incident is created. | string _(optional)_ |
| priority | The priority in ServiceNow. | string _(optional)_ |
| dest_ip | A list of destination IPs related to the security incident. The IPs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| source_ip | A list of source IPs related to the security incident. The IPs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| malware_hash | A list of malware hashes related to the security incident. The hashes will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| malware_url | A list of malware URLs related to the security incident. The URLs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| category | The category in ServiceNow. | string _(optional)_ |
| subcategory | The subcategory in ServiceNow. | string _(optional)_ |
| correlation_id | The correlation id of the security incident. | string _(optional)_ |
| correlation_display | The correlation display of the security incident. | string _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ---------------------------------------------- | ------ |
| externalId | The ID of the security incident in ServiceNow. | string |
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | ---------------------------------------------------- | -------- |
| fields | An array of fields. Example: `[priority, category]`. | string[] |
---
## ServiceNow ITOM
Refer to the [Run connector API documentation](https://www.elastic.co/guide/en/kibana/master/execute-connector-api.html#execute-connector-api-request-body)
for the full list of properties.
### `params`
| Property | Description | Type |
| --------------- | ----------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `addEvent`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (addEvent)`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| source | The name of the event source type. | string _(optional)_ |
| event_class | Specific instance of the source. | string _(optional)_ |
| resource | The name of the resource. | string _(optional)_ |
| node | The Host that the event was triggered for. | string _(optional)_ |
| metric_name | Name of the metric. | string _(optional)_ |
| type | The type of event. | string _(optional)_ |
| severity | The category in ServiceNow. | string _(optional)_ |
| description | The subcategory in ServiceNow. | string _(optional)_ |
| additional_info | Any additional information about the event. | string _(optional)_ |
| message_key | This value is used for de-duplication of events. All actions sharing this key will be associated with the same ServiceNow alert. | string _(optional)_ |
| time_of_event | The time of the event. | string _(optional)_ |
Refer to [ServiceNow documentation](https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html) for more information about the properties.
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | ------------------------------------------ | -------- |
| fields | An array of fields. Example: `[severity]`. | string[] |
---
## Jira
The [Jira user documentation `params`](https://www.elastic.co/guide/en/kibana/master/jira-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, and `getFields`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The Jira incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ----------- | ------------------------------------------------------------------------------------------------------- | --------------------- |
| summary | The title of the issue. | string |
| description | The description of the issue. | string _(optional)_ |
| externalId | The ID of the issue in Jira. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| issueType | The ID of the issue type in Jira. | string _(optional)_ |
| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ |
| labels | An array of labels. Labels cannot contain spaces. | string[] _(optional)_ |
| parent | The ID or key of the parent issue. Only for `Sub-task` issue types. | string _(optional)_ |
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ---------------------------- | ------ |
| externalId | The ID of the issue in Jira. | string |
#### `subActionParams (issueTypes)`
No parameters for the `issueTypes` subaction. Provide an empty object `{}`.
#### `subActionParams (fieldsByIssueType)`
| Property | Description | Type |
| -------- | --------------------------------- | ------ |
| id | The ID of the issue type in Jira. | string |
#### `subActionParams (issues)`
| Property | Description | Type |
| -------- | ------------------------ | ------ |
| title | The title to search for. | string |
#### `subActionParams (issue)`
| Property | Description | Type |
| -------- | ---------------------------- | ------ |
| id | The ID of the issue in Jira. | string |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
---
## IBM Resilient
The [IBM Resilient user documentation `params`](https://www.elastic.co/guide/en/kibana/master/resilient-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The IBM Resilient incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------- |
| name | The title of the incident. | string _(optional)_ |
| description | The description of the incident. | string _(optional)_ |
| externalId | The ID of the incident in IBM Resilient. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| incidentTypes | An array with the IDs of IBM Resilient incident types. | number[] _(optional)_ |
| severityCode | IBM Resilient ID of the severity code. | number _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (incidentTypes)`
No parameters for the `incidentTypes` subaction. Provide an empty object `{}`.
#### `subActionParams (severity)`
No parameters for the `severity` subaction. Provide an empty object `{}`.
---
## Swimlane
### `params`
| Property | Description | Type |
| --------------- | ---------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`. | string |
| subActionParams | The parameters of the subaction. | object |
`subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The Swimlane incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ----------- | -------------------------------- | ------------------- |
| alertId | The alert id. | string _(optional)_ |
| caseId | The case id of the incident. | string _(optional)_ |
| caseName | The case name of the incident. | string _(optional)_ |
| description | The description of the incident. | string _(optional)_ |
| ruleName | The rule name. | string _(optional)_ |
| severity | The severity of the incident. | string _(optional)_ |
---
# Command Line Utility
The [`kbn-action`](https://github.com/pmuellr/kbn-action) tool can be used to send HTTP requests to the Actions plugin. For instance, to create a Slack action from the `.slack` Action Type, use the following command:
@ -556,43 +228,3 @@ $ kbn-action create .slack "post to slack" '{"webhookUrl": "https://hooks.slack.
"version": "WzMsMV0="
}
```
# Developing New Action Types
When creating a new action type, your plugin will eventually call `server.plugins.actions.setup.registerType()` to register the type with the actions plugin, but there are some additional things to think about about and implement.
Consider working with the alerting team on early structure /design feedback of new actions, especially as the APIs and infrastructure are still under development.
Don't forget to ping @elastic/security-detections-response to see if the new connector should be enabled within their solution.
## licensing
Currently actions are licensed as "basic" if the action only interacts with the stack, eg the server log and es index actions. Other actions are at least "gold" level.
## plugin location
Currently actions that are licensed as "basic" **MUST** be implemented in the actions plugin, other actions can be implemented in any other plugin that pre-reqs the actions plugin. If the new action is generic across the stack, it probably belongs in the actions plugin, but if your action is very specific to a plugin/solution, it might be easiest to implement it in the plugin/solution. Keep in mind that if Kibana is run without the plugin being enabled, any actions defined in that plugin will not run, nor will those actions be available via APIs or UI.
Actions that take URLs or hostnames should check that those values are allowed. The allowed host list utilities are currently internal to the actions plugin, and so such actions will need to be implemented in the actions plugin. Longer-term, we will expose these utilities so they can be used by alerts implemented in other plugins; see [issue #64659](https://github.com/elastic/kibana/issues/64659).
## documentation
You should create asciidoc for the new action type. Add an entry to the action type index - [`docs/user/alerting/action-types.asciidoc`](../../../docs/user/alerting/action-types.asciidoc), which points to a new document for the action type that should be in the directory [`docs/user/alerting/action-types`](../../../docs/user/alerting/action-types).
We suggest following the template provided in `docs/action-type-template.asciidoc`. The [Email action type](https://www.elastic.co/guide/en/kibana/master/email-action-type.html) is an example of documentation created following the template.
## tests
The action type should have both jest tests and functional tests. For functional tests, if your action interacts with a 3rd party service via HTTP, you may be able to create a simulator for your service, to test with. See the existing functional test servers in the directory [`x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server`](../../test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server)
## action type config and secrets
Action types must define `config` and `secrets` which are used to create connectors. This data should be described with `@kbn/config-schema` object schemas, and you **MUST NOT** use `schema.maybe()` to define properties.
This is due to the fact that the structures are persisted in saved objects, which performs partial updates on the persisted data. If a property value is already persisted, but an update either doesn't include the property, or sets it to `undefined`, the persisted value will not be changed. Beyond this being a semantic error in general, it also ends up invalidating the encryption used to save secrets, and will render the secrets will not be able to be unencrypted later.
Instead of `schema.maybe()`, use `schema.nullable()`, which is the same as `schema.maybe()` except that when passed an `undefined` value, the object returned from the validation will be set to `null`. The resulting type will be `property-type | null`, whereas with `schema.maybe()` it would be `property-type | undefined`.
## user interface
To make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui).

View file

@ -13,16 +13,8 @@ export * from './alert_history_schema';
export * from './rewrite_request_case';
export * from './mustache_template';
export * from './validate_email_addresses';
export * from './servicenow_config';
export * from './connector_feature_config';
export const BASE_ACTION_API_PATH = '/api/actions';
export const INTERNAL_BASE_ACTION_API_PATH = '/internal/actions';
export const ACTIONS_FEATURE_ID = 'actions';
// supported values for `service` in addition to nodemailer's list of well-known services
export enum AdditionalEmailServices {
ELASTIC_CLOUD = 'elastic_cloud',
EXCHANGE = 'exchange_server',
OTHER = 'other',
}

View file

@ -7,6 +7,12 @@
import { LicenseType } from '@kbn/licensing-plugin/common/types';
export {
AlertingConnectorFeatureId,
CasesConnectorFeatureId,
UptimeConnectorFeatureId,
SecurityConnectorFeatureId,
} from './connector_feature_config';
export interface ActionType {
id: string;
name: string;

View file

@ -36,13 +36,13 @@ import {
} from './authorization/get_authorization_mode_by_source';
import { actionsAuthorizationMock } from './authorization/actions_authorization.mock';
import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption';
import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client';
import { ConnectorTokenClient } from './lib/connector_token_client';
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
import { Logger } from '@kbn/core/server';
import { connectorTokenClientMock } from './builtin_action_types/lib/connector_token_client.mock';
import { connectorTokenClientMock } from './lib/connector_token_client.mock';
import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock';
import { getOAuthJwtAccessToken } from './builtin_action_types/lib/get_oauth_jwt_access_token';
import { getOAuthClientCredentialsAccessToken } from './builtin_action_types/lib/get_oauth_client_credentials_access_token';
import { getOAuthJwtAccessToken } from './lib/get_oauth_jwt_access_token';
import { getOAuthClientCredentialsAccessToken } from './lib/get_oauth_client_credentials_access_token';
import { OAuthParams } from './routes/get_oauth_access_token';
jest.mock('@kbn/core-saved-objects-utils-server', () => {
@ -74,10 +74,10 @@ jest.mock('./authorization/get_authorization_mode_by_source', () => {
};
});
jest.mock('./builtin_action_types/lib/get_oauth_jwt_access_token', () => ({
jest.mock('./lib/get_oauth_jwt_access_token', () => ({
getOAuthJwtAccessToken: jest.fn(),
}));
jest.mock('./builtin_action_types/lib/get_oauth_client_credentials_access_token', () => ({
jest.mock('./lib/get_oauth_client_credentials_access_token', () => ({
getOAuthClientCredentialsAccessToken: jest.fn(),
}));

View file

@ -55,7 +55,7 @@ import {
} from './authorization/get_authorization_mode_by_source';
import { connectorAuditEvent, ConnectorAuditAction } from './lib/audit_events';
import { trackLegacyRBACExemption } from './lib/track_legacy_rbac_exemption';
import { isConnectorDeprecated } from './lib/is_conector_deprecated';
import { isConnectorDeprecated } from './lib/is_connector_deprecated';
import { ActionsConfigurationUtilities } from './actions_config';
import {
OAuthClientCredentialsParams,
@ -66,12 +66,12 @@ import {
getOAuthJwtAccessToken,
GetOAuthJwtConfig,
GetOAuthJwtSecrets,
} from './builtin_action_types/lib/get_oauth_jwt_access_token';
} from './lib/get_oauth_jwt_access_token';
import {
getOAuthClientCredentialsAccessToken,
GetOAuthClientCredentialsConfig,
GetOAuthClientCredentialsSecrets,
} from './builtin_action_types/lib/get_oauth_client_credentials_access_token';
} from './lib/get_oauth_client_credentials_access_token';
// We are assuming there won't be many actions. This is why we will load
// all the actions in advance and assume the total count to not go over 10000.

View file

@ -15,7 +15,7 @@ import { ActionsConfig, AllowedHosts, EnabledActionTypes, CustomHostSettings } f
import { getCanonicalCustomHostUrl } from './lib/custom_host_settings';
import { ActionTypeDisabledError } from './lib';
import { ProxySettings, ResponseSettings, SSLSettings } from './types';
import { getSSLSettingsFromConfig } from './builtin_action_types/lib/get_node_ssl_options';
import { getSSLSettingsFromConfig } from './lib/get_node_ssl_options';
import {
ValidateEmailAddressesOptions,
validateEmailAddresses,

View file

@ -1,65 +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 { ActionExecutor, TaskRunnerFactory } from '../lib';
import { ActionTypeRegistry } from '../action_type_registry';
import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks';
import { registerBuiltInActionTypes } from '.';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../actions_config.mock';
import { licenseStateMock } from '../lib/license_state.mock';
import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock';
const ACTION_TYPE_IDS = [
'.index',
'.email',
'.pagerduty',
'.server-log',
'.slack',
'.swimlane',
'.teams',
'.webhook',
'.xmatters',
];
export function createActionTypeRegistry(): {
logger: jest.Mocked<Logger>;
actionTypeRegistry: ActionTypeRegistry;
} {
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
const inMemoryMetrics = inMemoryMetricsMock.create();
const actionTypeRegistry = new ActionTypeRegistry({
taskManager: taskManagerMock.createSetup(),
licensing: licensingMock.createSetup(),
taskRunnerFactory: new TaskRunnerFactory(
new ActionExecutor({ isESOCanEncrypt: true }),
inMemoryMetrics
),
actionsConfigUtils: actionsConfigMock.create(),
licenseState: licenseStateMock.create(),
preconfiguredActions: [],
});
registerBuiltInActionTypes({
logger,
actionTypeRegistry,
actionsConfigUtils: actionsConfigMock.create(),
});
return { logger, actionTypeRegistry };
}
beforeEach(() => {
jest.resetAllMocks();
});
describe('action is registered', () => {
test('gets registered with builtin actions', () => {
const { actionTypeRegistry } = createActionTypeRegistry();
ACTION_TYPE_IDS.forEach((id) => expect(actionTypeRegistry.has(id)).toEqual(true));
});
});

View file

@ -1,86 +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 { Logger } from '@kbn/core/server';
import { ActionTypeRegistry } from '../action_type_registry';
import { ActionsConfigurationUtilities } from '../actions_config';
import { getActionType as getEmailActionType } from './email';
import { getActionType as getIndexActionType } from './es_index';
import { getActionType as getPagerDutyActionType } from './pagerduty';
import { getActionType as getSwimlaneActionType } from './swimlane';
import { getActionType as getServerLogActionType } from './server_log';
import { getActionType as getSlackActionType } from './slack';
import { getActionType as getWebhookActionType } from './webhook';
import { getActionType as getCasesWebhookActionType } from './cases_webhook';
import { getActionType as getXmattersActionType } from './xmatters';
import {
getServiceNowITSMActionType,
getServiceNowSIRActionType,
getServiceNowITOMActionType,
} from './servicenow';
import { getActionType as getJiraActionType } from './jira';
import { getActionType as getResilientActionType } from './resilient';
import { getActionType as getTeamsActionType } from './teams';
export type { ActionParamsType as EmailActionParams } from './email';
export { ActionTypeId as EmailActionTypeId } from './email';
export type { ActionParamsType as IndexActionParams } from './es_index';
export { ActionTypeId as IndexActionTypeId } from './es_index';
export type { ActionParamsType as PagerDutyActionParams } from './pagerduty';
export { ActionTypeId as PagerDutyActionTypeId } from './pagerduty';
export type { ActionParamsType as ServerLogActionParams } from './server_log';
export { ActionTypeId as ServerLogActionTypeId } from './server_log';
export type { ActionParamsType as SlackActionParams } from './slack';
export { ActionTypeId as SlackActionTypeId } from './slack';
export type { ActionParamsType as WebhookActionParams } from './webhook';
export type { ActionParamsType as CasesWebhookActionParams } from './cases_webhook';
export { ActionTypeId as CasesWebhookActionTypeId } from './cases_webhook';
export { ActionTypeId as WebhookActionTypeId } from './webhook';
export type { ActionParamsType as XmattersActionParams } from './xmatters';
export { ActionTypeId as XmattersActionTypeId } from './xmatters';
export type { ActionParamsType as ServiceNowActionParams } from './servicenow';
export {
ServiceNowITSMActionTypeId,
ServiceNowSIRActionTypeId,
ServiceNowITOMActionTypeId,
} from './servicenow';
export type { ActionParamsType as JiraActionParams } from './jira';
export { ActionTypeId as JiraActionTypeId } from './jira';
export type { ActionParamsType as ResilientActionParams } from './resilient';
export { ActionTypeId as ResilientActionTypeId } from './resilient';
export type { ActionParamsType as TeamsActionParams } from './teams';
export { ActionTypeId as TeamsActionTypeId } from './teams';
export function registerBuiltInActionTypes({
actionsConfigUtils: configurationUtilities,
actionTypeRegistry,
logger,
publicBaseUrl,
}: {
actionsConfigUtils: ActionsConfigurationUtilities;
actionTypeRegistry: ActionTypeRegistry;
logger: Logger;
publicBaseUrl?: string;
}) {
actionTypeRegistry.register(
getEmailActionType({ logger, configurationUtilities, publicBaseUrl })
);
actionTypeRegistry.register(getIndexActionType({ logger }));
actionTypeRegistry.register(getPagerDutyActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getSwimlaneActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServerLogActionType({ logger }));
actionTypeRegistry.register(getSlackActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getWebhookActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getCasesWebhookActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getXmattersActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServiceNowITSMActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServiceNowSIRActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServiceNowITOMActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getJiraActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getResilientActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getTeamsActionType({ logger, configurationUtilities }));
}

View file

@ -25,31 +25,6 @@ export type {
FindActionResult,
} from './types';
export type {
CasesWebhookActionTypeId,
CasesWebhookActionParams,
EmailActionTypeId,
EmailActionParams,
IndexActionTypeId,
IndexActionParams,
PagerDutyActionTypeId,
PagerDutyActionParams,
ServerLogActionTypeId,
ServerLogActionParams,
SlackActionTypeId,
SlackActionParams,
WebhookActionTypeId,
WebhookActionParams,
ServiceNowITSMActionTypeId,
ServiceNowSIRActionTypeId,
ServiceNowActionParams,
JiraActionTypeId,
JiraActionParams,
ResilientActionTypeId,
ResilientActionParams,
TeamsActionTypeId,
TeamsActionParams,
} from './builtin_action_types';
export type { PluginSetupContract, PluginStartContract } from './plugin';
export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib';

View file

@ -227,6 +227,7 @@ export class ActionExecutor {
secrets: validatedSecrets,
isEphemeral,
taskInfo,
configurationUtilities,
});
} catch (err) {
if (err.reason === ActionExecutionErrorReason.Validation) {

View file

@ -20,7 +20,7 @@ import {
} from './axios_utils';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../actions_config.mock';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
import { getCustomAgents } from './get_custom_agents';
const TestUrl = 'https://elastic.co/foo/bar/baz';

View file

@ -8,7 +8,7 @@
import { isObjectLike, isEmpty } from 'lodash';
import { AxiosInstance, Method, AxiosResponse, AxiosRequestConfig } from 'axios';
import { Logger } from '@kbn/core/server';
import { getCustomAgents } from '../builtin_action_types/lib/get_custom_agents';
import { getCustomAgents } from './get_custom_agents';
import { ActionsConfigurationUtilities } from '../actions_config';
export const request = async <T = unknown>({

View file

@ -10,7 +10,7 @@ import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mock
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
import { ConnectorTokenClient } from './connector_token_client';
import { Logger } from '@kbn/core/server';
import { ConnectorToken } from '../../types';
import { ConnectorToken } from '../types';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('@kbn/core-saved-objects-utils-server', () => {

View file

@ -8,8 +8,8 @@
import { omitBy, isUndefined } from 'lodash';
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
import { Logger, SavedObjectsClientContract, SavedObjectsUtils } from '@kbn/core/server';
import { ConnectorToken } from '../../types';
import { CONNECTOR_TOKEN_SAVED_OBJECT_TYPE } from '../../constants/saved_objects';
import { ConnectorToken } from '../types';
import { CONNECTOR_TOKEN_SAVED_OBJECT_TYPE } from '../constants/saved_objects';
export const MAX_TOKENS_RETURNED = 1;

View file

@ -7,11 +7,11 @@
import { LICENSE_TYPE } from '@kbn/licensing-plugin/common/types';
import { ActionType } from '../types';
import { ServerLogActionTypeId, IndexActionTypeId } from '../builtin_action_types';
import { ActionTypeConfig, ActionTypeSecrets, ActionTypeParams } from '../types';
const CASE_ACTION_TYPE_ID = '.case';
const ServerLogActionTypeId = '.server-log';
const IndexActionTypeId = '.index';
const ACTIONS_SCOPED_WITHIN_STACK = new Set([
ServerLogActionTypeId,
IndexActionTypeId,

View file

@ -11,7 +11,7 @@ import { HttpsProxyAgent } from 'https-proxy-agent';
import { Logger } from '@kbn/core/server';
import { getCustomAgents } from './get_custom_agents';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
const targetHost = 'elastic.co';

View file

@ -10,7 +10,7 @@ import { Agent as HttpsAgent, AgentOptions } from 'https';
import HttpProxyAgent from 'http-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { Logger } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ActionsConfigurationUtilities } from '../actions_config';
import { getNodeSSLOptions, getSSLSettingsFromConfig } from './get_node_ssl_options';
interface GetCustomAgentsResponse {

View file

@ -7,7 +7,7 @@
import { PeerCertificate } from 'tls';
import { Logger } from '@kbn/core/server';
import { SSLSettings } from '../../types';
import { SSLSettings } from '../types';
export function getNodeSSLOptions(
logger: Logger,

View file

@ -8,7 +8,7 @@ import sinon from 'sinon';
import { Logger } from '@kbn/core/server';
import { asyncForEach } from '@kbn/std';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
import { connectorTokenClientMock } from './connector_token_client.mock';
import { getOAuthClientCredentialsAccessToken } from './get_oauth_client_credentials_access_token';
import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token';

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import { Logger } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ConnectorToken, ConnectorTokenClientContract } from '../../types';
import { ActionsConfigurationUtilities } from '../actions_config';
import { ConnectorToken, ConnectorTokenClientContract } from '../types';
import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token';
export interface GetOAuthClientCredentialsConfig {

View file

@ -8,7 +8,7 @@ import sinon from 'sinon';
import { Logger } from '@kbn/core/server';
import { asyncForEach } from '@kbn/std';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
import { connectorTokenClientMock } from './connector_token_client.mock';
import { getOAuthJwtAccessToken } from './get_oauth_jwt_access_token';
import { createJWTAssertion } from './create_jwt_assertion';

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import { Logger } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ConnectorToken, ConnectorTokenClientContract } from '../../types';
import { ActionsConfigurationUtilities } from '../actions_config';
import { ConnectorToken, ConnectorTokenClientContract } from '../types';
import { createJWTAssertion } from './create_jwt_assertion';
import { requestOAuthJWTToken } from './request_oauth_jwt_token';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { isConnectorDeprecated } from './is_conector_deprecated';
import { isConnectorDeprecated } from './is_connector_deprecated';
describe('isConnectorDeprecated', () => {
const connector = {

View file

@ -40,7 +40,7 @@ export const isConnectorDeprecated = (
* the usesTableApi property to true to all connectors prior 7.16. Pre configured connectors
* cannot be migrated. This check ensures that pre configured connectors without the
* usesTableApi property explicitly in the kibana.yml file are considered deprecated.
* According to the schema defined here x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts
* According to the schema defined here x-pack/plugins/stack_connector/server/connector_types/servicenow/schema.ts
* if the property is not defined it will be set to true at the execution of the connector.
*/
if (!Object.hasOwn(connector.config, 'usesTableApi')) {
@ -51,7 +51,7 @@ export const isConnectorDeprecated = (
* Connector created prior to 7.16 will be migrated to have the usesTableApi property set to true.
* Connectors created after 7.16 should have the usesTableApi property set to true or false.
* If the usesTableApi is omitted on an API call it will be defaulted to true. Check the schema
* here x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts.
* here x-pack/plugins/stack_connector/server/connector_types/servicenow/schema.ts.
* The !! is to make TS happy.
*/
return !!connector.config.usesTableApi;

View file

@ -11,7 +11,7 @@ jest.mock('axios', () => ({
import axios from 'axios';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token';
const createAxiosInstanceMock = axios.create as jest.Mock;

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { Logger } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ActionsConfigurationUtilities } from '../actions_config';
import { OAuthTokenResponse, requestOAuthToken } from './request_oauth_token';
import { RewriteResponseCase } from '../../../common';
import { RewriteResponseCase } from '../../common';
export const OAUTH_CLIENT_CREDENTIALS_GRANT_TYPE = 'client_credentials';

View file

@ -11,7 +11,7 @@ jest.mock('axios', () => ({
import axios from 'axios';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
import { requestOAuthJWTToken } from './request_oauth_jwt_token';
const createAxiosInstanceMock = axios.create as jest.Mock;

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import { Logger } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ActionsConfigurationUtilities } from '../actions_config';
import { OAuthTokenResponse, requestOAuthToken } from './request_oauth_token';
import { RewriteResponseCase } from '../../../common';
import { RewriteResponseCase } from '../../common';
// This is a standard for JSON Web Token (JWT) Profile
// for OAuth 2.0 Client Authentication and Authorization Grants https://datatracker.ietf.org/doc/html/rfc7523#section-8.1

View file

@ -12,7 +12,7 @@ import axios from 'axios';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { requestOAuthToken } from './request_oauth_token';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '../actions_config.mock';
const createAxiosInstanceMock = axios.create as jest.Mock;
const axiosInstanceMock = jest.fn();

View file

@ -9,9 +9,9 @@ import qs from 'query-string';
import axios from 'axios';
import stringify from 'json-stable-stringify';
import { Logger } from '@kbn/core/server';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { AsApiContract } from '../../../common';
import { request } from './axios_utils';
import { ActionsConfigurationUtilities } from '../actions_config';
import { AsApiContract } from '../../common';
export interface OAuthTokenResponse {
tokenType: string;

View file

@ -16,7 +16,7 @@ import { actionsClientMock } from './actions_client.mock';
import { PluginSetupContract, PluginStartContract, renderActionParameterTemplates } from './plugin';
import { Services } from './types';
import { actionsAuthorizationMock } from './authorization/actions_authorization.mock';
import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client';
import { ConnectorTokenClient } from './lib/connector_token_client';
export { actionsAuthorizationMock };
export { actionsClientMock };
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;

View file

@ -15,7 +15,7 @@ import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks';
import { eventLogMock } from '@kbn/event-log-plugin/server/mocks';
import { ActionType, ActionsApiRequestHandlerContext } from './types';
import { ActionType, ActionsApiRequestHandlerContext, ExecutorType } from './types';
import { ActionsConfig } from './config';
import {
ActionsPlugin,
@ -25,6 +25,10 @@ import {
} from './plugin';
import { AlertHistoryEsIndexConnectorId } from '../common';
const executor: ExecutorType<{}, {}, {}, void> = async (options) => {
return { status: 'ok', actionId: options.actionId };
};
describe('Actions Plugin', () => {
describe('setup()', () => {
let context: PluginInitializerContext;
@ -383,7 +387,15 @@ describe('Actions Plugin', () => {
setup(getConfig());
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await plugin.setup(coreSetup as any, pluginsSetup);
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
pluginSetup.registerType({
id: '.server-log',
name: 'Server log',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
executor,
});
const pluginStart = await plugin.start(coreStart, pluginsStart);
expect(pluginStart.preconfiguredActions.length).toEqual(1);
@ -393,7 +405,16 @@ describe('Actions Plugin', () => {
it('should handle preconfiguredAlertHistoryEsIndex = true', async () => {
setup(getConfig({ preconfiguredAlertHistoryEsIndex: true }));
await plugin.setup(coreSetup, pluginsSetup);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
pluginSetup.registerType({
id: '.index',
name: 'ES Index',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
executor,
});
const pluginStart = await plugin.start(coreStart, pluginsStart);
expect(pluginStart.preconfiguredActions.length).toEqual(2);

View file

@ -49,7 +49,6 @@ import {
createEphemeralExecutionEnqueuerFunction,
createBulkExecutionEnqueuerFunction,
} from './create_execute_function';
import { registerBuiltInActionTypes } from './builtin_action_types';
import { registerActionsUsageCollector } from './usage';
import {
ActionExecutor,
@ -92,12 +91,12 @@ import { getAlertHistoryEsIndex } from './preconfigured_connectors/alert_history
import { createAlertHistoryIndexTemplate } from './preconfigured_connectors/alert_history_es_index/create_alert_history_index_template';
import { ACTIONS_FEATURE_ID, AlertHistoryEsIndexConnectorId } from '../common';
import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER } from './constants/event_log';
import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client';
import { ConnectorTokenClient } from './lib/connector_token_client';
import { InMemoryMetrics, registerClusterCollector, registerNodeCollector } from './monitoring';
import {
isConnectorDeprecated,
ConnectorWithOptionalDeprecation,
} from './lib/is_conector_deprecated';
} from './lib/is_connector_deprecated';
import { createSubActionConnectorFramework } from './sub_action_framework';
import { IServiceAbstract, SubActionConnectorType } from './sub_action_framework/types';
import { SubActionConnector } from './sub_action_framework/sub_action_connector';
@ -275,13 +274,6 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
this.preconfiguredActions
);
registerBuiltInActionTypes({
logger: this.logger,
actionTypeRegistry,
actionsConfigUtils,
publicBaseUrl: core.http.basePath.publicBaseUrl,
});
const usageCollection = plugins.usageCollection;
if (usageCollection) {
registerActionsUsageCollector(

View file

@ -7,9 +7,9 @@
import { i18n } from '@kbn/i18n';
import { PreConfiguredAction } from '../../types';
import { ActionTypeId as EsIndexActionTypeId } from '../../builtin_action_types/es_index';
import { AlertHistoryEsIndexConnectorId, AlertHistoryDefaultIndexName } from '../../../common';
const EsIndexActionTypeId = '.index';
export function getAlertHistoryEsIndex(): Readonly<PreConfiguredAction> {
return Object.freeze({
name: i18n.translate('xpack.actions.alertHistoryEsIndexConnector.name', {

View file

@ -1,175 +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 { getWellKnownEmailServiceRoute } from './get_well_known_email_service';
import { httpServiceMock } from '@kbn/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { mockHandlerArguments } from './legacy/_mock_handler_arguments';
import { verifyAccessAndContext } from './verify_access_and_context';
jest.mock('./verify_access_and_context', () => ({
verifyAccessAndContext: jest.fn(),
}));
beforeEach(() => {
jest.resetAllMocks();
(verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler);
});
describe('getWellKnownEmailServiceRoute', () => {
it('returns config for well known email service', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getWellKnownEmailServiceRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(
`"/internal/actions/connector/_email_config/{service}"`
);
const [context, req, res] = mockHandlerArguments(
{},
{
params: { service: 'gmail' },
},
['ok']
);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"host": "smtp.gmail.com",
"port": 465,
"secure": true,
},
}
`);
expect(res.ok).toHaveBeenCalledWith({
body: {
host: 'smtp.gmail.com',
port: 465,
secure: true,
},
});
});
it('returns config for elastic cloud email service', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getWellKnownEmailServiceRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(
`"/internal/actions/connector/_email_config/{service}"`
);
const [context, req, res] = mockHandlerArguments(
{},
{
params: { service: 'elastic_cloud' },
},
['ok']
);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {
"host": "dockerhost",
"port": 10025,
"secure": false,
},
}
`);
expect(res.ok).toHaveBeenCalledWith({
body: {
host: 'dockerhost',
port: 10025,
secure: false,
},
});
});
it('returns empty for unknown service', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getWellKnownEmailServiceRoute(router, licenseState);
const [config, handler] = router.get.mock.calls[0];
expect(config.path).toMatchInlineSnapshot(
`"/internal/actions/connector/_email_config/{service}"`
);
const [context, req, res] = mockHandlerArguments(
{},
{
params: { service: 'foo' },
},
['ok']
);
expect(await handler(context, req, res)).toMatchInlineSnapshot(`
Object {
"body": Object {},
}
`);
expect(res.ok).toHaveBeenCalledWith({
body: {},
});
});
it('ensures the license allows getting well known email service config', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
getWellKnownEmailServiceRoute(router, licenseState);
const [, handler] = router.get.mock.calls[0];
const [context, req, res] = mockHandlerArguments(
{},
{
params: { service: 'gmail' },
},
['ok']
);
await handler(context, req, res);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
it('ensures the license check prevents getting well known email service config', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
(verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => {
throw new Error('OMG');
});
getWellKnownEmailServiceRoute(router, licenseState);
const [, handler] = router.get.mock.calls[0];
const [context, req, res] = mockHandlerArguments(
{},
{
params: { service: 'gmail' },
},
['ok']
);
expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`);
expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function));
});
});

View file

@ -1,57 +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 { schema } from '@kbn/config-schema';
import { IRouter } from '@kbn/core/server';
import nodemailerGetService from 'nodemailer/lib/well-known';
import SMTPConnection from 'nodemailer/lib/smtp-connection';
import { ILicenseState } from '../lib';
import { AdditionalEmailServices, INTERNAL_BASE_ACTION_API_PATH } from '../../common';
import { ActionsRequestHandlerContext } from '../types';
import { verifyAccessAndContext } from './verify_access_and_context';
import { ELASTIC_CLOUD_SERVICE } from '../builtin_action_types/email';
const paramSchema = schema.object({
service: schema.string(),
});
export const getWellKnownEmailServiceRoute = (
router: IRouter<ActionsRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${INTERNAL_BASE_ACTION_API_PATH}/connector/_email_config/{service}`,
validate: {
params: paramSchema,
},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const { service } = req.params;
let response: SMTPConnection.Options = {};
if (service === AdditionalEmailServices.ELASTIC_CLOUD) {
response = ELASTIC_CLOUD_SERVICE;
} else {
const serviceEntry = nodemailerGetService(service);
if (serviceEntry) {
response = {
host: serviceEntry.host,
port: serviceEntry.port,
secure: serviceEntry.secure,
};
}
}
return res.ok({
body: response,
});
})
)
);
};

View file

@ -16,7 +16,6 @@ import { getActionRoute } from './get';
import { getAllActionRoute } from './get_all';
import { connectorTypesRoute } from './connector_types';
import { updateActionRoute } from './update';
import { getWellKnownEmailServiceRoute } from './get_well_known_email_service';
import { getOAuthAccessToken } from './get_oauth_access_token';
import { defineLegacyRoutes } from './legacy';
import { ActionsConfigurationUtilities } from '../actions_config';
@ -42,5 +41,4 @@ export function defineRoutes(opts: RouteOptions) {
executeActionRoute(router, licenseState);
getOAuthAccessToken(router, licenseState, actionsConfigUtils);
getWellKnownEmailServiceRoute(router, licenseState);
}

View file

@ -6,36 +6,26 @@
*/
import { transformConnectorsForExport } from './transform_connectors_for_export';
import { ActionTypeRegistry, ActionTypeRegistryOpts } from '../action_type_registry';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../actions_config.mock';
import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks';
import { ActionExecutor, TaskRunnerFactory } from '../lib';
import { registerBuiltInActionTypes } from '../builtin_action_types';
import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock';
import { actionTypeRegistryMock } from '../action_type_registry.mock';
import { ActionType, ActionTypeRegistryContract, ActionTypeSecrets } from '../types';
describe('transform connector for export', () => {
const inMemoryMetrics = inMemoryMetricsMock.create();
const actionTypeRegistryParams: ActionTypeRegistryOpts = {
licensing: licensingMock.createSetup(),
taskManager: taskManagerMock.createSetup(),
taskRunnerFactory: new TaskRunnerFactory(
new ActionExecutor({ isESOCanEncrypt: true }),
inMemoryMetrics
),
actionsConfigUtils: actionsConfigMock.create(),
licenseState: licenseStateMock.create(),
preconfiguredActions: [],
const connectorType: jest.Mocked<ActionType> = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
executor: jest.fn(),
validate: {
secrets: {
schema: {
validate: (value: unknown) => value as ActionTypeSecrets,
},
},
},
};
const actionTypeRegistry: ActionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
registerBuiltInActionTypes({
logger: loggingSystemMock.create().get(),
actionTypeRegistry,
actionsConfigUtils: actionsConfigMock.create(),
});
const actionTypeRegistry: jest.Mocked<ActionTypeRegistryContract> =
actionTypeRegistryMock.create();
const connectorsWithNoSecrets = [
{
@ -239,6 +229,7 @@ describe('transform connector for export', () => {
];
it('should not change connectors without secrets', () => {
actionTypeRegistry.get.mockReturnValue(connectorType);
expect(transformConnectorsForExport(connectorsWithNoSecrets, actionTypeRegistry)).toEqual(
connectorsWithNoSecrets.map((connector) => ({
...connector,
@ -251,6 +242,18 @@ describe('transform connector for export', () => {
});
it('should remove secrets for connectors with secrets', () => {
actionTypeRegistry.get.mockReturnValue({
...connectorType,
validate: {
secrets: {
schema: {
validate: (value: unknown) => {
throw new Error('i need secrets!');
},
},
},
},
});
expect(transformConnectorsForExport(connectorsWithSecrets, actionTypeRegistry)).toEqual(
connectorsWithSecrets.map((connector) => ({
...connector,

View file

@ -7,13 +7,12 @@
import { SavedObject } from '@kbn/core/server';
import { ActionsConfigurationUtilities } from '../actions_config';
import { ActionTypeRegistry } from '../action_type_registry';
import { validateSecrets } from '../lib';
import { RawAction, ActionType } from '../types';
import { RawAction, ActionType, ActionTypeRegistryContract } from '../types';
export function transformConnectorsForExport(
connectors: SavedObject[],
actionTypeRegistry: ActionTypeRegistry
actionTypeRegistry: ActionTypeRegistryContract
): Array<SavedObject<RawAction>> {
return connectors.map((c) => {
const connector = c as SavedObject<RawAction>;

View file

@ -64,6 +64,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
});
expect(res).toEqual({
@ -84,6 +85,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
});
expect(res).toEqual({
@ -104,6 +106,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
});
expect(res).toEqual({
@ -122,6 +125,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
});
expect(res).toEqual({
@ -135,7 +139,14 @@ describe('Executor', () => {
const executor = createExecutor(TestNoSubActions);
await expect(async () =>
executor({ actionId, params, config, secrets, services })
executor({
actionId,
params,
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
})
).rejects.toThrowError('You should register at least one subAction for your connector type');
});
@ -149,6 +160,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
})
).rejects.toThrowError(
'Sub action "not-exist" is not registered. Connector id: test-action-id. Connector name: Test. Connector type: .test'
@ -165,6 +177,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
})
).rejects.toThrowError(
'Method "not-exist" does not exists in service. Sub action: "testUrl". Connector id: test-action-id. Connector name: Test. Connector type: .test'
@ -181,6 +194,7 @@ describe('Executor', () => {
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
})
).rejects.toThrowError(
'Method "notAFunction" must be a function. Connector id: test-action-id. Connector name: Test. Connector type: .test'
@ -191,7 +205,14 @@ describe('Executor', () => {
const executor = createExecutor(TestExecutor);
await expect(async () =>
executor({ actionId, params: { ...params, subAction: 'echo' }, config, secrets, services })
executor({
actionId,
params: { ...params, subAction: 'echo' },
config,
secrets,
services,
configurationUtilities: mockedActionsConfig,
})
).rejects.toThrowError(
'Request validation failed (Error: [id]: expected value of type [string] but got [undefined])'
);

View file

@ -20,14 +20,10 @@ import { PluginSetupContract, PluginStartContract } from './plugin';
import { ActionsClient } from './actions_client';
import { ActionTypeExecutorResult } from '../common';
import { TaskInfo } from './lib/action_executor';
import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client';
import { ConnectorTokenClient } from './lib/connector_token_client';
import { ActionsConfigurationUtilities } from './actions_config';
export type { ActionTypeExecutorResult, ActionTypeExecutorRawResult } from '../common';
export type { GetFieldsByIssueTypeResponse as JiraGetFieldsResponse } from './builtin_action_types/jira/types';
export type { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './builtin_action_types/servicenow/types';
export type { GetCommonFieldsResponse as ResilientGetFieldsResponse } from './builtin_action_types/resilient/types';
export type { SwimlanePublicConfigurationType } from './builtin_action_types/swimlane/types';
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
export type GetServicesFunction = (request: KibanaRequest) => Services;
export type ActionTypeRegistryContract = PublicMethodsOf<ActionTypeRegistry>;
@ -66,6 +62,7 @@ export interface ActionTypeExecutorOptions<Config, Secrets, Params> {
params: Params;
isEphemeral?: boolean;
taskInfo?: TaskInfo;
configurationUtilities: ActionsConfigurationUtilities;
}
export interface ActionResult<Config extends ActionTypeConfig = ActionTypeConfig> {

View file

@ -8,16 +8,16 @@
import {
PushToServiceApiParams as JiraPushToServiceApiParams,
Incident as JiraIncident,
} from '@kbn/actions-plugin/server/builtin_action_types/jira/types';
} from '@kbn/stack-connectors-plugin/server/connector_types/cases/jira/types';
import {
PushToServiceApiParams as ResilientPushToServiceApiParams,
Incident as ResilientIncident,
} from '@kbn/actions-plugin/server/builtin_action_types/resilient/types';
} from '@kbn/stack-connectors-plugin/server/connector_types/cases/resilient/types';
import {
PushToServiceApiParamsITSM as ServiceNowITSMPushToServiceApiParams,
PushToServiceApiParamsSIR as ServiceNowSIRPushToServiceApiParams,
ServiceNowITSMIncident,
} from '@kbn/actions-plugin/server/builtin_action_types/servicenow/types';
} from '@kbn/stack-connectors-plugin/server/connector_types/cases/servicenow/types';
import { UserProfile } from '@kbn/security-plugin/common';
import { CaseResponse, ConnectorMappingsAttributes } from '../../../common/api';

View file

@ -24,6 +24,7 @@
{ "path": "../actions/tsconfig.json" },
{ "path": "../rule_registry/tsconfig.json" },
{ "path": "../triggers_actions_ui/tsconfig.json"},
{ "path": "../stack_connectors/tsconfig.json"},
{ "path": "../../../src/plugins/es_ui_shared/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_utils/tsconfig.json" },

View file

@ -568,12 +568,12 @@ export const LEGACY_RULES = [
/**
* Matches the id for the built-in in email action type
* See x-pack/plugins/actions/server/builtin_action_types/email.ts
* See x-pack/plugins/stack_connectors/server/connector_types/stack/email/index.ts
*/
export const ALERT_ACTION_TYPE_EMAIL = '.email';
/**
* Matches the id for the built-in in log action type
* See x-pack/plugins/actions/server/builtin_action_types/log.ts
* See x-pack/plugins/stack_connectors/server/connector_types/stack/server_log/index.ts
*/
export const ALERT_ACTION_TYPE_LOG = '.server-log';

View file

@ -0,0 +1,373 @@
# Stack Connectors
The `stack_connectors` plugin provides connector types shipped with Kibana, built on top of the framework provided in the `actions` plugin.
---
Table of Contents
- [Connector Types](#connector-types)
- [ServiceNow ITSM](#servicenow-itsm)
- [`params`](#params)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice)
- [`subActionParams (getFields)`](#subactionparams-getfields)
- [`subActionParams (getIncident)`](#subactionparams-getincident)
- [`subActionParams (getChoices)`](#subactionparams-getchoices)
- [ServiceNow Sec Ops](#servicenow-sec-ops)
- [`params`](#params-1)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1)
- [`subActionParams (getFields)`](#subactionparams-getfields-1)
- [`subActionParams (getIncident)`](#subactionparams-getincident-1)
- [`subActionParams (getChoices)`](#subactionparams-getchoices-1)
- [ServiceNow ITOM](#servicenow-itom)
- [`params`](#params-2)
- [`subActionParams (addEvent)`](#subactionparams-addevent)
- [`subActionParams (getChoices)`](#subactionparams-getchoices-2)
- [Jira](#jira)
- [`params`](#params-3)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2)
- [`subActionParams (getIncident)`](#subactionparams-getincident-2)
- [`subActionParams (issueTypes)`](#subactionparams-issuetypes)
- [`subActionParams (fieldsByIssueType)`](#subactionparams-fieldsbyissuetype)
- [`subActionParams (issues)`](#subactionparams-issues)
- [`subActionParams (issue)`](#subactionparams-issue)
- [`subActionParams (getFields)`](#subactionparams-getfields-2)
- [IBM Resilient](#ibm-resilient)
- [`params`](#params-4)
- [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3)
- [`subActionParams (getFields)`](#subactionparams-getfields-3)
- [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes)
- [`subActionParams (severity)`](#subactionparams-severity)
- [Swimlane](#swimlane)
- [`params`](#params-5)
- [| severity | The severity of the incident. | string _(optional)_ |](#-severity-----the-severity-of-the-incident-----string-optional-)
- [Developing New Connector Types](#developing-new-connector-types)
- [licensing](#licensing)
- [plugin location](#plugin-location)
- [documentation](#documentation)
- [tests](#tests)
- [connector type config and secrets](#connector-type-config-and-secrets)
- [user interface](#user-interface)
# Connector Types
Kibana ships with a set of built-in connector types. See [Connectors Documentation](https://www.elastic.co/guide/en/kibana/master/action-types.html).
In addition to the documented configurations, several built in connector type offer additional `params` configurations.
## ServiceNow ITSM
The [ServiceNow ITSM user documentation `params`](https://www.elastic.co/guide/en/kibana/master/servicenow-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `getIncident`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The ServiceNow incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------- |
| short_description | The title of the incident. | string |
| description | The description of the incident. | string _(optional)_ |
| externalId | The ID of the incident in ServiceNow. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| severity | The severity in ServiceNow. | string _(optional)_ |
| urgency | The urgency in ServiceNow. | string _(optional)_ |
| impact | The impact in ServiceNow. | string _(optional)_ |
| category | The category in ServiceNow. | string _(optional)_ |
| subcategory | The subcategory in ServiceNow. | string _(optional)_ |
| correlation_id | The correlation id of the incident. | string _(optional)_ |
| correlation_display | The correlation display of the ServiceNow. | string _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ------------------------------------- | ------ |
| externalId | The ID of the incident in ServiceNow. | string |
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | -------------------------------------------------- | -------- |
| fields | An array of fields. Example: `[category, impact]`. | string[] |
---
## ServiceNow Sec Ops
The [ServiceNow SecOps user documentation `params`](https://www.elastic.co/guide/en/kibana/master/servicenow-sir-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `getIncident`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The ServiceNow security incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| short_description | The title of the security incident. | string |
| description | The description of the security incident. | string _(optional)_ |
| externalId | The ID of the security incident in ServiceNow. If present, the security incident is updated. Otherwise, a new security incident is created. | string _(optional)_ |
| priority | The priority in ServiceNow. | string _(optional)_ |
| dest_ip | A list of destination IPs related to the security incident. The IPs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| source_ip | A list of source IPs related to the security incident. The IPs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| malware_hash | A list of malware hashes related to the security incident. The hashes will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| malware_url | A list of malware URLs related to the security incident. The URLs will be added as observables to the security incident. | (string \| string[]) _(optional)_ |
| category | The category in ServiceNow. | string _(optional)_ |
| subcategory | The subcategory in ServiceNow. | string _(optional)_ |
| correlation_id | The correlation id of the security incident. | string _(optional)_ |
| correlation_display | The correlation display of the security incident. | string _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ---------------------------------------------- | ------ |
| externalId | The ID of the security incident in ServiceNow. | string |
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | ---------------------------------------------------- | -------- |
| fields | An array of fields. Example: `[priority, category]`. | string[] |
---
## ServiceNow ITOM
The [ServiceNow ITOM user documentation `params`](https://www.elastic.co/guide/en/kibana/master/servicenow-itom-action-type.html) lists configuration properties for the `addEvent` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | ----------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `addEvent`, and `getChoices`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (addEvent)`
| Property | Description | Type |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| source | The name of the event source type. | string _(optional)_ |
| event_class | Specific instance of the source. | string _(optional)_ |
| resource | The name of the resource. | string _(optional)_ |
| node | The Host that the event was triggered for. | string _(optional)_ |
| metric_name | Name of the metric. | string _(optional)_ |
| type | The type of event. | string _(optional)_ |
| severity | The category in ServiceNow. | string _(optional)_ |
| description | The subcategory in ServiceNow. | string _(optional)_ |
| additional_info | Any additional information about the event. | string _(optional)_ |
| message_key | This value is used for de-duplication of events. All actions sharing this key will be associated with the same ServiceNow alert. | string _(optional)_ |
| time_of_event | The time of the event. | string _(optional)_ |
Refer to [ServiceNow documentation](https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html) for more information about the properties.
#### `subActionParams (getChoices)`
| Property | Description | Type |
| -------- | ------------------------------------------ | -------- |
| fields | An array of fields. Example: `[severity]`. | string[] |
---
## Jira
The [Jira user documentation `params`](https://www.elastic.co/guide/en/kibana/master/jira-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getIncident`, `issueTypes`, `fieldsByIssueType`, `issues`, `issue`, and `getFields`. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The Jira incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ----------- | ------------------------------------------------------------------------------------------------------- | --------------------- |
| summary | The title of the issue. | string |
| description | The description of the issue. | string _(optional)_ |
| externalId | The ID of the issue in Jira. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| issueType | The ID of the issue type in Jira. | string _(optional)_ |
| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ |
| labels | An array of labels. Labels cannot contain spaces. | string[] _(optional)_ |
| parent | The ID or key of the parent issue. Only for `Sub-task` issue types. | string _(optional)_ |
#### `subActionParams (getIncident)`
| Property | Description | Type |
| ---------- | ---------------------------- | ------ |
| externalId | The ID of the issue in Jira. | string |
#### `subActionParams (issueTypes)`
No parameters for the `issueTypes` subaction. Provide an empty object `{}`.
#### `subActionParams (fieldsByIssueType)`
| Property | Description | Type |
| -------- | --------------------------------- | ------ |
| id | The ID of the issue type in Jira. | string |
#### `subActionParams (issues)`
| Property | Description | Type |
| -------- | ------------------------ | ------ |
| title | The title to search for. | string |
#### `subActionParams (issue)`
| Property | Description | Type |
| -------- | ---------------------------- | ------ |
| id | The ID of the issue in Jira. | string |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
---
## IBM Resilient
The [IBM Resilient user documentation `params`](https://www.elastic.co/guide/en/kibana/master/resilient-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available.
### `params`
| Property | Description | Type |
| --------------- | ------------------------------------------------------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`, `getFields`, `incidentTypes`, and `severity. | string |
| subActionParams | The parameters of the subaction. | object |
#### `subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The IBM Resilient incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------- |
| name | The title of the incident. | string _(optional)_ |
| description | The description of the incident. | string _(optional)_ |
| externalId | The ID of the incident in IBM Resilient. If present, the incident is updated. Otherwise, a new incident is created. | string _(optional)_ |
| incidentTypes | An array with the IDs of IBM Resilient incident types. | number[] _(optional)_ |
| severityCode | IBM Resilient ID of the severity code. | number _(optional)_ |
#### `subActionParams (getFields)`
No parameters for the `getFields` subaction. Provide an empty object `{}`.
#### `subActionParams (incidentTypes)`
No parameters for the `incidentTypes` subaction. Provide an empty object `{}`.
#### `subActionParams (severity)`
No parameters for the `severity` subaction. Provide an empty object `{}`.
---
## Swimlane
Refer to the [Run connector API documentation](https://www.elastic.co/guide/en/kibana/master/execute-connector-api.html#execute-connector-api-request-body)
for the full list of properties.
### `params`
| Property | Description | Type |
| --------------- | ---------------------------------------------------- | ------ |
| subAction | The subaction to perform. It can be `pushToService`. | string |
| subActionParams | The parameters of the subaction. | object |
`subActionParams (pushToService)`
| Property | Description | Type |
| -------- | ------------------------------------------------------------------------------------------------------------- | --------------------- |
| incident | The Swimlane incident. | object |
| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ |
The following table describes the properties of the `incident` object.
| Property | Description | Type |
| ----------- | -------------------------------- | ------------------- |
| alertId | The alert id. | string _(optional)_ |
| caseId | The case id of the incident. | string _(optional)_ |
| caseName | The case name of the incident. | string _(optional)_ |
| description | The description of the incident. | string _(optional)_ |
| ruleName | The rule name. | string _(optional)_ |
| severity | The severity of the incident. | string _(optional)_ |
---
# Developing New Connector Types
When creating a new connector type, your plugin will eventually call `server.plugins.actions.setup.registerType()` to register the type with the `actions` plugin, but there are some additional things to think about about and implement.
Consider working with the alerting team on early structure /design feedback of new connectors, especially as the APIs and infrastructure are still under development.
Don't forget to ping @elastic/security-detections-response to see if the new connector should be enabled within their solution.
## Licensing
Currently connectors are licensed as "basic" if the connector only interacts with the stack, eg the server log and es index connectors. Other connectors are at least "gold" level.
## Plugin location
If the new connector is generic across the stack, it probably belongs in the `stack_connectors` plugin, but if your connector is very specific to a plugin/solution, it might be easiest to implement it in that plugin/solution.
Connectors that take URLs or hostnames should check that those values are allowed by using the allowed host utilities in the `actions` plugin.
## Documentation
You should create asciidoc for the new connector type. Add an entry to the connector type index - [`docs/user/alerting/action-types.asciidoc`](../../../docs/user/alerting/action-types.asciidoc), which points to a new document for the connector type that should be in the directory [`docs/user/alerting/action-types`](../../../docs/user/alerting/action-types).
We suggest following the template provided in `docs/action-type-template.asciidoc`. The [Email action type](https://www.elastic.co/guide/en/kibana/master/email-action-type.html) is an example of documentation created following the template.
## Tests
The connector type should have both unit tests and functional tests. For functional tests, if your connector interacts with a 3rd party service via HTTP, you may be able to create a simulator for your service to test with. See the existing functional test servers in the directory [`x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server`](../../test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server)
## Connector type config and secrets
Connector types must define `config` and `secrets` which are used to create connectors. This data should be described with `@kbn/config-schema` object schemas, and you **MUST NOT** use `schema.maybe()` to define properties.
This is due to the fact that the structures are persisted in saved objects, which performs partial updates on the persisted data. If a property value is already persisted, but an update either doesn't include the property, or sets it to `undefined`, the persisted value will not be changed. Beyond this being a semantic error in general, it also ends up invalidating the encryption used to save secrets, and will render the secrets unable to be unencrypted later.
Instead of `schema.maybe()`, use `schema.nullable()`, which is the same as `schema.maybe()` except that when passed an `undefined` value, the object returned from the validation will be set to `null`. The resulting type will be `property-type | null`, whereas with `schema.maybe()` it would be `property-type | undefined`.
## User interface
To make this connector usable in the Kibana UI, you will need to provide all the UI editing aspects of the connector. The existing connector type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui).

View file

@ -0,0 +1,15 @@
/*
* 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.
*/
// supported values for `service` in addition to nodemailer's list of well-known services
export enum AdditionalEmailServices {
ELASTIC_CLOUD = 'elastic_cloud',
EXCHANGE = 'exchange_server',
OTHER = 'other',
}
export const INTERNAL_BASE_STACK_CONNECTORS_API_PATH = '/internal/stack_connectors';

View file

@ -8,10 +8,6 @@
export const serviceNowITSMTable = 'incident';
export const serviceNowSIRTable = 'sn_si_incident';
export const ServiceNowITSMActionTypeId = '.servicenow';
export const ServiceNowSIRActionTypeId = '.servicenow-sir';
export const ServiceNowITOMActionTypeId = '.servicenow-itom';
const SN_ITSM_APP_ID = '7148dbc91bf1f450ced060a7234bcb88';
const SN_SIR_APP_ID = '2f0746801baeb01019ae54e4604bcb0f';

View file

@ -0,0 +1,17 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/x-pack/plugins/stack_connectors'],
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/x-pack/plugins/stack_connectors',
coverageReporters: ['text', 'html'],
collectCoverageFrom: [
'<rootDir>/x-pack/plugins/stack_connectors/{common,server}/**/*.{js,ts,tsx}',
],
};

View file

@ -0,0 +1,13 @@
{
"id": "stackConnectors",
"owner": {
"name": "Response Ops",
"githubTeam": "response-ops"
},
"server": true,
"version": "8.0.0",
"kibanaVersion": "kibana",
"configPath": ["xpack", "stack_connectors"],
"requiredPlugins": ["actions"],
"ui": false
}

View file

@ -7,7 +7,12 @@
import { curry } from 'lodash';
import { Logger } from '@kbn/core/server';
import { CasesConnectorFeatureId } from '../../../common';
import type {
ActionType as ConnectorType,
ActionTypeExecutorOptions as ConnectorTypeExecutorOptions,
ActionTypeExecutorResult as ConnectorTypeExecutorResult,
} from '@kbn/actions-plugin/server/types';
import { CasesConnectorFeatureId } from '@kbn/actions-plugin/common/connector_feature_config';
import {
CasesWebhookActionParamsType,
CasesWebhookExecutorResultData,
@ -16,8 +21,6 @@ import {
ExecutorParams,
ExecutorSubActionPushParams,
} from './types';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { createExternalService } from './service';
import {
ExecutorParamsSchema,
@ -30,22 +33,20 @@ import * as i18n from './translations';
const supportedSubActions: string[] = ['pushToService'];
export type ActionParamsType = CasesWebhookActionParamsType;
export const ActionTypeId = '.cases-webhook';
// action type definition
export function getActionType({
export const ConnectorTypeId = '.cases-webhook';
// connector type definition
export function getConnectorType({
logger,
configurationUtilities,
}: {
logger: Logger;
configurationUtilities: ActionsConfigurationUtilities;
}): ActionType<
}): ConnectorType<
CasesWebhookPublicConfigurationType,
CasesWebhookSecretConfigurationType,
ExecutorParams,
CasesWebhookExecutorResultData
> {
return {
id: ActionTypeId,
id: ConnectorTypeId,
minimumLicenseRequired: 'gold',
name: i18n.NAME,
validate: {
@ -62,24 +63,22 @@ export function getActionType({
},
connector: validate.connector,
},
executor: curry(executor)({ logger, configurationUtilities }),
executor: curry(executor)({ logger }),
supportedFeatureIds: [CasesConnectorFeatureId],
};
}
// action executor
export async function executor(
{
logger,
configurationUtilities,
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
execOptions: ActionTypeExecutorOptions<
{ logger }: { logger: Logger },
execOptions: ConnectorTypeExecutorOptions<
CasesWebhookPublicConfigurationType,
CasesWebhookSecretConfigurationType,
CasesWebhookActionParamsType
>
): Promise<ActionTypeExecutorResult<CasesWebhookExecutorResultData>> {
): Promise<ConnectorTypeExecutorResult<CasesWebhookExecutorResultData>> {
const actionId = execOptions.actionId;
const configurationUtilities = execOptions.configurationUtilities;
const { subAction, subActionParams } = execOptions.params;
let data: CasesWebhookExecutorResultData | undefined;

View file

@ -7,7 +7,7 @@
import { schema } from '@kbn/config-schema';
import { CasesWebhookMethods } from './types';
import { nullableType } from '../lib/nullable';
import { nullableType } from '../../lib/nullable';
const HeadersSchema = schema.recordOf(schema.string(), schema.string());

View file

@ -8,15 +8,15 @@
import axios, { AxiosError, AxiosResponse } from 'axios';
import { createExternalService } from './service';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { request, createAxiosResponse } from '@kbn/actions-plugin/server/lib/axios_utils';
import { CasesWebhookMethods, CasesWebhookPublicConfigurationType, ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => {
const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -9,8 +9,10 @@ import axios, { AxiosResponse } from 'axios';
import { Logger } from '@kbn/core/server';
import { isString } from 'lodash';
import { renderMustacheStringNoEscape } from '@kbn/actions-plugin/server/lib/mustache_renderer';
import { request } from '@kbn/actions-plugin/server/lib/axios_utils';
import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config';
import { validateAndNormalizeUrl, validateJson } from './validators';
import { renderMustacheStringNoEscape } from '../../lib/mustache_renderer';
import {
createServiceError,
getObjectValueByKeyAsString,
@ -31,8 +33,6 @@ import {
} from './types';
import * as i18n from './translations';
import { request } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
export const createExternalService = (
actionId: string,

View file

@ -7,12 +7,12 @@
import { i18n } from '@kbn/i18n';
export const NAME = i18n.translate('xpack.actions.builtin.cases.casesWebhookTitle', {
export const NAME = i18n.translate('xpack.stackConnectors.casesWebhook.title', {
defaultMessage: 'Webhook - Case Management',
});
export const INVALID_URL = (err: string, url: string) =>
i18n.translate('xpack.actions.builtin.casesWebhook.casesWebhookConfigurationErrorNoHostname', {
i18n.translate('xpack.stackConnectors.casesWebhook.configurationErrorNoHostname', {
defaultMessage: 'error configuring cases webhook action: unable to parse {url}: {err}',
values: {
err,
@ -21,7 +21,7 @@ export const INVALID_URL = (err: string, url: string) =>
});
export const CONFIG_ERR = (err: string) =>
i18n.translate('xpack.actions.builtin.casesWebhook.casesWebhookConfigurationError', {
i18n.translate('xpack.stackConnectors.casesWebhook.configurationError', {
defaultMessage: 'error configuring cases webhook action: {err}',
values: {
err,
@ -29,14 +29,14 @@ export const CONFIG_ERR = (err: string) =>
});
export const INVALID_USER_PW = i18n.translate(
'xpack.actions.builtin.casesWebhook.invalidUsernamePassword',
'xpack.stackConnectors.casesWebhook.invalidUsernamePassword',
{
defaultMessage: 'both user and password must be specified',
}
);
export const ALLOWED_HOSTS_ERROR = (message: string) =>
i18n.translate('xpack.actions.builtin.casesWebhook.configuration.apiAllowedHostsError', {
i18n.translate('xpack.stackConnectors.casesWebhook.configuration.apiAllowedHostsError', {
defaultMessage: 'error configuring connector action: {message}',
values: {
message,

View file

@ -7,7 +7,7 @@
import { TypeOf } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import { ValidatorServices } from '../../types';
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import {
ExecutorParamsSchema,
ExecutorSubActionPushParamsSchema,

View file

@ -7,7 +7,7 @@
import { AxiosResponse, AxiosError } from 'axios';
import { isEmpty, isObjectLike, get } from 'lodash';
import { getErrorMessage } from '../../lib/axios_utils';
import { getErrorMessage } from '@kbn/actions-plugin/server/lib/axios_utils';
import * as i18n from './translations';
export const createServiceError = (error: AxiosError, message: string) => {

View file

@ -5,14 +5,14 @@
* 2.0.
*/
import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config';
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import * as i18n from './translations';
import { ActionsConfigurationUtilities } from '../../actions_config';
import {
CasesWebhookPublicConfigurationType,
CasesWebhookSecretConfigurationType,
ExternalServiceValidation,
} from './types';
import { ValidatorServices } from '../../types';
const validateConfig = (
configObject: CasesWebhookPublicConfigurationType,

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
export {
getConnectorType as getCasesWebhookConnectorType,
ConnectorTypeId as CasesWebhookConnectorTypeId,
} from './cases_webhook';
export type { ActionParamsType as CasesWebhookActionParams } from './cases_webhook';
export {
getConnectorType as getJiraConnectorType,
ConnectorTypeId as JiraConnectorTypeId,
} from './jira';
export type { ActionParamsType as JiraActionParams } from './jira';
export {
getConnectorType as getResilientConnectorType,
ConnectorTypeId as ResilientConnectorTypeId,
} from './resilient';
export type { ActionParamsType as ResilientActionParams } from './resilient';
export {
getServiceNowITSMConnectorType,
getServiceNowSIRConnectorType,
getServiceNowITOMConnectorType,
ServiceNowITSMConnectorTypeId,
ServiceNowSIRConnectorTypeId,
ServiceNowITOMConnectorTypeId,
} from './servicenow';
export type { ActionParamsType as ServiceNowActionParams } from './servicenow';
export { getConnectorType as getSwimlaneConnectorType } from './swimlane';
export {
getConnectorType as getXmattersConnectorType,
ConnectorTypeId as XmattersConnectorTypeId,
} from './xmatters';
export type { ActionParamsType as XmattersActionParams } from './xmatters';

View file

@ -9,14 +9,23 @@ import { curry } from 'lodash';
import { TypeOf } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import type {
ActionType as ConnectorType,
ActionTypeExecutorOptions as ConnectorTypeExecutorOptions,
ActionTypeExecutorResult as ConnectorTypeExecutorResult,
} from '@kbn/actions-plugin/server/types';
import {
AlertingConnectorFeatureId,
CasesConnectorFeatureId,
UptimeConnectorFeatureId,
SecurityConnectorFeatureId,
} from '@kbn/actions-plugin/common/types';
import { validate } from './validators';
import {
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
ExecutorParamsSchema,
} from './schema';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types';
import { createExternalService } from './service';
import { api } from './api';
import {
@ -32,17 +41,10 @@ import {
ExecutorSubActionGetIncidentParams,
} from './types';
import * as i18n from './translations';
import {
AlertingConnectorFeatureId,
CasesConnectorFeatureId,
UptimeConnectorFeatureId,
SecurityConnectorFeatureId,
} from '../../../common';
export type ActionParamsType = TypeOf<typeof ExecutorParamsSchema>;
interface GetActionTypeParams {
interface GetConnectorTypeParams {
logger: Logger;
configurationUtilities: ActionsConfigurationUtilities;
}
const supportedSubActions: string[] = [
@ -55,19 +57,19 @@ const supportedSubActions: string[] = [
'issue',
];
export const ActionTypeId = '.jira';
// action type definition
export function getActionType(
params: GetActionTypeParams
): ActionType<
export const ConnectorTypeId = '.jira';
// connector type definition
export function getConnectorType(
params: GetConnectorTypeParams
): ConnectorType<
JiraPublicConfigurationType,
JiraSecretConfigurationType,
ExecutorParams,
JiraExecutorResultData | {}
> {
const { logger, configurationUtilities } = params;
const { logger } = params;
return {
id: ActionTypeId,
id: ConnectorTypeId,
minimumLicenseRequired: 'gold',
name: i18n.NAME,
supportedFeatureIds: [
@ -89,23 +91,20 @@ export function getActionType(
schema: ExecutorParamsSchema,
},
},
executor: curry(executor)({ logger, configurationUtilities }),
executor: curry(executor)({ logger }),
};
}
// action executor
async function executor(
{
logger,
configurationUtilities,
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
execOptions: ActionTypeExecutorOptions<
{ logger }: { logger: Logger },
execOptions: ConnectorTypeExecutorOptions<
JiraPublicConfigurationType,
JiraSecretConfigurationType,
ExecutorParams
>
): Promise<ActionTypeExecutorResult<JiraExecutorResultData | {}>> {
const { actionId, config, params, secrets } = execOptions;
): Promise<ConnectorTypeExecutorResult<JiraExecutorResultData | {}>> {
const { actionId, config, params, secrets, configurationUtilities } = execOptions;
const { subAction, subActionParams } = params as ExecutorParams;
let data: JiraExecutorResultData | null = null;

View file

@ -8,11 +8,11 @@
import axios from 'axios';
import { createExternalService } from './service';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { request, createAxiosResponse } from '@kbn/actions-plugin/server/lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
interface ResponseError extends Error {
@ -20,8 +20,8 @@ interface ResponseError extends Error {
}
jest.mock('axios');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => {
const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -9,6 +9,12 @@ import axios from 'axios';
import { isEmpty } from 'lodash';
import { Logger } from '@kbn/core/server';
import {
request,
getErrorMessage,
throwIfResponseIsNotValid,
} from '@kbn/actions-plugin/server/lib/axios_utils';
import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config';
import {
CreateCommentParams,
CreateIncidentParams,
@ -27,8 +33,6 @@ import {
} from './types';
import * as i18n from './translations';
import { request, getErrorMessage, throwIfResponseIsNotValid } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
const VERSION = '2';
const BASE_URL = `rest/api/${VERSION}`;

View file

@ -7,12 +7,12 @@
import { i18n } from '@kbn/i18n';
export const NAME = i18n.translate('xpack.actions.builtin.cases.jiraTitle', {
export const NAME = i18n.translate('xpack.stackConnectors.jira.title', {
defaultMessage: 'Jira',
});
export const ALLOWED_HOSTS_ERROR = (message: string) =>
i18n.translate('xpack.actions.builtin.jira.configuration.apiAllowedHostsError', {
i18n.translate('xpack.stackConnectors.jira.configuration.apiAllowedHostsError', {
defaultMessage: 'error configuring connector action: {message}',
values: {
message,

View file

@ -9,6 +9,7 @@
import { TypeOf } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import {
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
@ -22,7 +23,6 @@ import {
ExecutorSubActionGetIssueParamsSchema,
ExecutorSubActionCommonFieldsParamsSchema,
} from './schema';
import { ValidatorServices } from '../../types';
export type JiraPublicConfigurationType = TypeOf<typeof ExternalIncidentServiceConfigurationSchema>;
export type JiraSecretConfigurationType = TypeOf<

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import {
JiraPublicConfigurationType,
JiraSecretConfigurationType,
@ -12,7 +13,6 @@ import {
} from './types';
import * as i18n from './translations';
import { ValidatorServices } from '../../types';
export const validateCommonConfig = (
configObject: JiraPublicConfigurationType,

View file

@ -9,14 +9,22 @@ import { curry } from 'lodash';
import { TypeOf } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import type {
ActionType as ConnectorType,
ActionTypeExecutorOptions as ConnectorTypeExecutorOptions,
ActionTypeExecutorResult as ConnectorTypeExecutorResult,
} from '@kbn/actions-plugin/server/types';
import {
AlertingConnectorFeatureId,
CasesConnectorFeatureId,
SecurityConnectorFeatureId,
} from '@kbn/actions-plugin/common/types';
import { validate } from './validators';
import {
ExternalIncidentServiceConfigurationSchema,
ExternalIncidentServiceSecretConfigurationSchema,
ExecutorParamsSchema,
} from './schema';
import { ActionsConfigurationUtilities } from '../../actions_config';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types';
import { createExternalService } from './service';
import { api } from './api';
import {
@ -30,34 +38,28 @@ import {
ExecutorSubActionCommonFieldsParams,
} from './types';
import * as i18n from './translations';
import {
AlertingConnectorFeatureId,
CasesConnectorFeatureId,
SecurityConnectorFeatureId,
} from '../../../common';
export type ActionParamsType = TypeOf<typeof ExecutorParamsSchema>;
interface GetActionTypeParams {
interface GetConnectorTypeParams {
logger: Logger;
configurationUtilities: ActionsConfigurationUtilities;
}
const supportedSubActions: string[] = ['getFields', 'pushToService', 'incidentTypes', 'severity'];
export const ActionTypeId = '.resilient';
// action type definition
export function getActionType(
params: GetActionTypeParams
): ActionType<
export const ConnectorTypeId = '.resilient';
// connector type definition
export function getConnectorType(
params: GetConnectorTypeParams
): ConnectorType<
ResilientPublicConfigurationType,
ResilientSecretConfigurationType,
ExecutorParams,
ResilientExecutorResultData | {}
> {
const { logger, configurationUtilities } = params;
const { logger } = params;
return {
id: ActionTypeId,
id: ConnectorTypeId,
minimumLicenseRequired: 'platinum',
name: i18n.NAME,
supportedFeatureIds: [
@ -78,23 +80,20 @@ export function getActionType(
schema: ExecutorParamsSchema,
},
},
executor: curry(executor)({ logger, configurationUtilities }),
executor: curry(executor)({ logger }),
};
}
// action executor
async function executor(
{
logger,
configurationUtilities,
}: { logger: Logger; configurationUtilities: ActionsConfigurationUtilities },
execOptions: ActionTypeExecutorOptions<
{ logger }: { logger: Logger },
execOptions: ConnectorTypeExecutorOptions<
ResilientPublicConfigurationType,
ResilientSecretConfigurationType,
ExecutorParams
>
): Promise<ActionTypeExecutorResult<ResilientExecutorResultData | {}>> {
const { actionId, config, params, secrets } = execOptions;
): Promise<ConnectorTypeExecutorResult<ResilientExecutorResultData | {}>> {
const { actionId, config, params, secrets, configurationUtilities } = execOptions;
const { subAction, subActionParams } = params as ExecutorParams;
let data: ResilientExecutorResultData | null = null;

View file

@ -8,18 +8,18 @@
import axios from 'axios';
import { createExternalService, getValueTextContent, formatUpdateRequest } from './service';
import { request, createAxiosResponse } from '../../lib/axios_utils';
import { request, createAxiosResponse } from '@kbn/actions-plugin/server/lib/axios_utils';
import { ExternalService } from './types';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { incidentTypes, resilientFields, severity } from './mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
jest.mock('axios');
jest.mock('../../lib/axios_utils', () => {
const originalUtils = jest.requireActual('../../lib/axios_utils');
jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => {
const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils');
return {
...originalUtils,
request: jest.fn(),

View file

@ -9,6 +9,12 @@ import axios from 'axios';
import { omitBy, isNil } from 'lodash/fp';
import { Logger } from '@kbn/core/server';
import {
getErrorMessage,
request,
throwIfResponseIsNotValid,
} from '@kbn/actions-plugin/server/lib/axios_utils';
import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config';
import {
ExternalServiceCredentials,
ExternalService,
@ -24,8 +30,6 @@ import {
} from './types';
import * as i18n from './translations';
import { getErrorMessage, request, throwIfResponseIsNotValid } from '../../lib/axios_utils';
import { ActionsConfigurationUtilities } from '../../actions_config';
const VIEW_INCIDENT_URL = `#incidents`;

View file

@ -7,12 +7,12 @@
import { i18n } from '@kbn/i18n';
export const NAME = i18n.translate('xpack.actions.builtin.cases.resilientTitle', {
export const NAME = i18n.translate('xpack.stackConnectors.resilient.title', {
defaultMessage: 'IBM Resilient',
});
export const ALLOWED_HOSTS_ERROR = (message: string) =>
i18n.translate('xpack.actions.builtin.configuration.apiAllowedHostsError', {
i18n.translate('xpack.stackConnectors.resilient.configuration.apiAllowedHostsError', {
defaultMessage: 'error configuring connector action: {message}',
values: {
message,

View file

@ -9,6 +9,7 @@
import { TypeOf } from '@kbn/config-schema';
import { Logger } from '@kbn/core/server';
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import {
ExecutorParamsSchema,
ExecutorSubActionCommonFieldsParamsSchema,
@ -21,8 +22,6 @@ import {
ExternalIncidentServiceSecretConfigurationSchema,
} from './schema';
import { ValidatorServices } from '../../types';
export type ResilientPublicConfigurationType = TypeOf<
typeof ExternalIncidentServiceConfigurationSchema
>;

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { ValidatorServices } from '@kbn/actions-plugin/server/types';
import {
ResilientPublicConfigurationType,
ResilientSecretConfigurationType,
@ -12,7 +13,6 @@ import {
} from './types';
import * as i18n from './translations';
import { ValidatorServices } from '../../types';
export const validateCommonConfig = (
configObject: ResilientPublicConfigurationType,

View file

@ -10,9 +10,9 @@ import { SNProductsConfig } from './types';
export const serviceNowITSMTable = 'incident';
export const serviceNowSIRTable = 'sn_si_incident';
export const ServiceNowITSMActionTypeId = '.servicenow';
export const ServiceNowSIRActionTypeId = '.servicenow-sir';
export const ServiceNowITOMActionTypeId = '.servicenow-itom';
export const ServiceNowITSMConnectorTypeId = '.servicenow';
export const ServiceNowSIRConnectorTypeId = '.servicenow-sir';
export const ServiceNowITOMConnectorTypeId = '.servicenow-itom';
const SN_ITSM_APP_ID = '7148dbc91bf1f450ced060a7234bcb88';
const SN_SIR_APP_ID = '2f0746801baeb01019ae54e4604bcb0f';

View file

@ -9,8 +9,8 @@ import axios from 'axios';
import { createServiceWrapper } from './create_service_wrapper';
import { Logger } from '@kbn/core/server';
import { loggingSystemMock } from '@kbn/core/server/mocks';
import { actionsConfigMock } from '../../actions_config.mock';
import { connectorTokenClientMock } from '../lib/connector_token_client.mock';
import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock';
import { connectorTokenClientMock } from '@kbn/actions-plugin/server/lib/connector_token_client.mock';
import { snExternalServiceConfig } from './config';
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;

Some files were not shown because too many files have changed in this diff Show more