Skip to content

Commit

Permalink
[ResponseOps] provide config to turn off email action footer
Browse files Browse the repository at this point in the history
  • Loading branch information
pmuellr committed Apr 17, 2023
1 parent 5464614 commit c1a10fd
Show file tree
Hide file tree
Showing 18 changed files with 96 additions and 20 deletions.
3 changes: 3 additions & 0 deletions docs/settings/alert-action-settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ A list of allowed email domains which can be used with the email connector. When

WARNING: This feature is available in {kib} 7.17.4 and 8.3.0 onwards but is not supported in {kib} 8.0, 8.1 or 8.2. As such, this setting should be removed before upgrading from 7.17 to 8.0, 8.1 or 8.2. It is possible to configure the settings in 7.17.4 and then upgrade to 8.3.0 directly.

`xpack.actions.enableFooterInEmail` {ess-icon}::
A boolean value indicating that a footer with a relevant link should be added to emails sent as alerting actions. Default: true.

`xpack.actions.enabledActionTypes` {ess-icon}::
A list of action types that are enabled. It defaults to `[*]`, enabling all types. The names for built-in {kib} action types are prefixed with a `.` and include: `.email`, `.index`, `.jira`, `.opsgenie`, `.pagerduty`, `.resilient`, `.server-log`, `.servicenow`, .`servicenow-itom`, `.servicenow-sir`, `.slack`, `.swimlane`, `.teams`, `.tines`, `.torq`, `.xmatters`, and `.webhook`. An empty list `[]` will disable all action types.
+
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ describe('create()', () => {
verificationMode: 'full',
proxyVerificationMode: 'full',
},
enableFooterInEmail: true,
});

const localActionTypeRegistryParams = {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/actions_config.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const createActionsConfigMock = () => {
getMicrosoftGraphApiUrl: jest.fn().mockReturnValue(undefined),
validateEmailAddresses: jest.fn().mockReturnValue(undefined),
getMaxAttempts: jest.fn().mockReturnValue(3),
enableFooterInEmail: jest.fn().mockReturnValue(true),
};
return mocked;
};
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/actions_config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const defaultActionsConfig: ActionsConfig = {
proxyVerificationMode: 'full',
verificationMode: 'full',
},
enableFooterInEmail: true,
};

describe('ensureUriAllowed', () => {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/actions/server/actions_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface ActionsConfigurationUtilities {
addresses: string[],
options?: ValidateEmailAddressesOptions
): string | undefined;
enableFooterInEmail: () => boolean;
}

function allowListErrorMessage(field: AllowListingField, value: string) {
Expand Down Expand Up @@ -215,5 +216,6 @@ export function getActionsConfigurationUtilities(
DEFAULT_MAX_ATTEMPTS
);
},
enableFooterInEmail: () => config.enableFooterInEmail,
};
}
3 changes: 3 additions & 0 deletions x-pack/plugins/actions/server/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('config validation', () => {
"allowedHosts": Array [
"*",
],
"enableFooterInEmail": true,
"enabledActionTypes": Array [
"*",
],
Expand Down Expand Up @@ -57,6 +58,7 @@ describe('config validation', () => {
"allowedHosts": Array [
"*",
],
"enableFooterInEmail": true,
"enabledActionTypes": Array [
"*",
],
Expand Down Expand Up @@ -198,6 +200,7 @@ describe('config validation', () => {
"allowedHosts": Array [
"*",
],
"enableFooterInEmail": true,
"enabledActionTypes": Array [
"*",
],
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const configSchema = schema.object({
connectorTypeOverrides: schema.maybe(schema.arrayOf(connectorTypeSchema)),
})
),
enableFooterInEmail: schema.boolean({ defaultValue: true }),
});

export type ActionsConfig = TypeOf<typeof configSchema>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ const BaseActionsConfig: ActionsConfig = {
maxResponseContentLength: ByteSizeValue.parse('1mb'),
responseTimeout: momentDuration(1000 * 30),
customHostSettings: undefined,
enableFooterInEmail: true,
};

function getACUfromConfig(config: Partial<ActionsConfig> = {}): ActionsConfigurationUtilities {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ const BaseActionsConfig: ActionsConfig = {
maxResponseContentLength: ByteSizeValue.parse('1mb'),
responseTimeout: momentDuration(1000 * 30),
customHostSettings: undefined,
enableFooterInEmail: true,
};

function getACUfromConfig(config: Partial<ActionsConfig> = {}): ActionsConfigurationUtilities {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe('custom_host_settings', () => {
rejectUnauthorized: true,
maxResponseContentLength: new ByteSizeValue(1000000),
responseTimeout: moment.duration(60000),
enableFooterInEmail: true,
};

test('ensure it copies over the config parts that it does not touch', () => {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/actions/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('Actions Plugin', () => {
rejectUnauthorized: true,
maxResponseContentLength: new ByteSizeValue(1000000),
responseTimeout: moment.duration(60000),
enableFooterInEmail: true,
});
plugin = new ActionsPlugin(context);
coreSetup = coreMock.createSetup();
Expand Down Expand Up @@ -213,6 +214,7 @@ describe('Actions Plugin', () => {
rejectUnauthorized: true,
maxResponseContentLength: new ByteSizeValue(1000000),
responseTimeout: moment.duration('60s'),
enableFooterInEmail: true,
...overrides,
};
}
Expand Down Expand Up @@ -268,6 +270,7 @@ describe('Actions Plugin', () => {
rejectUnauthorized: true,
maxResponseContentLength: new ByteSizeValue(1000000),
responseTimeout: moment.duration(60000),
enableFooterInEmail: true,
});
plugin = new ActionsPlugin(context);
coreSetup = coreMock.createSetup();
Expand Down Expand Up @@ -336,6 +339,7 @@ describe('Actions Plugin', () => {
rejectUnauthorized: true,
maxResponseContentLength: new ByteSizeValue(1000000),
responseTimeout: moment.duration('60s'),
enableFooterInEmail: true,
...overrides,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,10 @@ describe('execute()', () => {
logger: mockedLogger,
};

beforeEach(() => {
executorOptions.configurationUtilities = actionsConfigMock.create();
});

test('ensure parameters are as expected', async () => {
sendEmailMock.mockReset();
const result = await connectorType.executor(executorOptions);
Expand All @@ -540,7 +544,7 @@ describe('execute()', () => {
"content": Object {
"message": "a message to you
--
---
This message was sent by Elastic.",
"subject": "the subject",
Expand Down Expand Up @@ -591,7 +595,7 @@ describe('execute()', () => {
"content": Object {
"message": "a message to you
--
---
This message was sent by Elastic.",
"subject": "the subject",
Expand Down Expand Up @@ -642,7 +646,7 @@ describe('execute()', () => {
"content": Object {
"message": "a message to you
--
---
This message was sent by Elastic.",
"subject": "the subject",
Expand Down Expand Up @@ -738,6 +742,28 @@ describe('execute()', () => {
`);
});

test('provides no footer link when enableFooterInEmail is false', async () => {
const customExecutorOptions: EmailConnectorTypeExecutorOptions = {
...executorOptions,
configurationUtilities: {
...configurationUtilities,
enableFooterInEmail: jest.fn().mockReturnValue(false),
},
};

const connectorTypeWithPublicUrl = getConnectorType({
publicBaseUrl: 'https://localhost:1234/foo/bar',
});

await connectorTypeWithPublicUrl.executor(customExecutorOptions);

expect(customExecutorOptions.configurationUtilities.enableFooterInEmail).toHaveBeenCalledTimes(
1
);
const sendMailCall = sendEmailMock.mock.calls[0][1];
expect(sendMailCall.content.message).toMatchInlineSnapshot(`"a message to you"`);
});

test('provides a footer link to Elastic when publicBaseUrl is defined', async () => {
const connectorTypeWithPublicUrl = getConnectorType({
publicBaseUrl: 'https://localhost:1234/foo/bar',
Expand All @@ -750,7 +776,7 @@ describe('execute()', () => {
expect(sendMailCall.content.message).toMatchInlineSnapshot(`
"a message to you
--
---
This message was sent by Elastic. [Go to Elastic](https://localhost:1234/foo/bar)."
`);
Expand Down Expand Up @@ -779,7 +805,7 @@ describe('execute()', () => {
expect(sendMailCall.content.message).toMatchInlineSnapshot(`
"a message to you
--
---
This message was sent by Elastic. [View this in Elastic](https://localhost:1234/foo/bar/my/app)."
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const ELASTIC_CLOUD_SERVICE: SMTPConnection.Options = {
secure: false,
};

const EMAIL_FOOTER_DIVIDER = '\n\n--\n\n';
const EMAIL_FOOTER_DIVIDER = '\n\n---\n\n';

const ConfigSchemaProps = {
service: schema.string({ defaultValue: 'other' }),
Expand Down Expand Up @@ -319,10 +319,14 @@ async function executor(
transport.service = config.service;
}

const footerMessage = getFooterMessage({
publicBaseUrl,
kibanaFooterLink: params.kibanaFooterLink,
});
let actualMessage = params.message;
if (configurationUtilities.enableFooterInEmail()) {
const footerMessage = getFooterMessage({
publicBaseUrl,
kibanaFooterLink: params.kibanaFooterLink,
});
actualMessage = `${params.message}${EMAIL_FOOTER_DIVIDER}${footerMessage}`;
}

const sendEmailOptions: SendEmailOptions = {
connectorId: actionId,
Expand All @@ -335,7 +339,7 @@ async function executor(
},
content: {
subject: params.subject,
message: `${params.message}${EMAIL_FOOTER_DIVIDER}${footerMessage}`,
message: actualMessage,
},
hasAuth: config.hasAuth,
configurationUtilities,
Expand Down
3 changes: 3 additions & 0 deletions x-pack/test/alerting_api_integration/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface CreateTestConfigOptions {
testFiles?: string[];
reportName?: string;
useDedicatedTaskRunner: boolean;
enableFooterInEmail?: boolean;
}

// test.not-enabled is specifically not enabled
Expand Down Expand Up @@ -75,6 +76,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
testFiles = undefined,
reportName = undefined,
useDedicatedTaskRunner,
enableFooterInEmail = true,
} = options;

return async ({ readConfigFile }: FtrConfigProviderContext) => {
Expand Down Expand Up @@ -173,6 +175,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
'some.non.existent.com',
'smtp.live.com',
])}`,
`--xpack.actions.enableFooterInEmail=${enableFooterInEmail}`,
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
'--xpack.alerting.invalidateApiKeysTask.interval="15s"',
'--xpack.alerting.healthCheck.interval="1s"',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function initPlugin(router: IRouter, path: string) {
cc: null,
bcc: null,
subject: 'email-subject',
html: `<p>email-message</p>\n<p>--</p>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
html: `<p>email-message</p>\n<hr>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
text: 'email-message\n\n--\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).',
headers: {},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ export default function emailTest({ getService }: FtrProviderContext) {
cc: null,
bcc: null,
subject: 'email-subject',
html: `<p>email-message</p>\n<p>--</p>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
text: 'email-message\n\n--\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).',
html: `<p>email-message</p>\n<hr>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
text: 'email-message\n\n---\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).',
headers: {},
},
});
Expand All @@ -147,10 +147,10 @@ export default function emailTest({ getService }: FtrProviderContext) {
.then((resp: any) => {
const { text, html } = resp.body.data.message;
expect(text).to.eql(
'_italic_ **bold** https://elastic.co link\n\n--\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).'
'_italic_ **bold** https://elastic.co link\n\n---\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).'
);
expect(html).to.eql(
`<p><em>italic</em> <strong>bold</strong> <a href="https://elastic.co">https://elastic.co</a> link</p>\n<p>--</p>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`
`<p><em>italic</em> <strong>bold</strong> <a href="https://elastic.co">https://elastic.co</a> link</p>\n<hr>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`
);
});
});
Expand All @@ -174,10 +174,10 @@ export default function emailTest({ getService }: FtrProviderContext) {
.then((resp: any) => {
const { text, html } = resp.body.data.message;
expect(text).to.eql(
'message\n\n--\n\nThis message was sent by Elastic. [View my path in Elastic](https://localhost:5601/my/path).'
'message\n\n---\n\nThis message was sent by Elastic. [View my path in Elastic](https://localhost:5601/my/path).'
);
expect(html).to.eql(
`<p>message</p>\n<p>--</p>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601/my/path\">View my path in Elastic</a>.</p>\n`
`<p>message</p>\n<hr>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601/my/path\">View my path in Elastic</a>.</p>\n`
);
});
});
Expand Down Expand Up @@ -325,8 +325,8 @@ export default function emailTest({ getService }: FtrProviderContext) {
cc: null,
bcc: null,
subject: 'email-subject',
html: `<p>email-message</p>\n<p>--</p>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
text: 'email-message\n\n--\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).',
html: `<p>email-message</p>\n<hr>\n<p>This message was sent by Elastic. <a href=\"https://localhost:5601\">Go to Elastic</a>.</p>\n`,
text: 'email-message\n\n---\n\nThis message was sent by Elastic. [Go to Elastic](https://localhost:5601).',
headers: {},
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export default createTestConfig('spaces_only', {
useDedicatedTaskRunner: true,
testFiles: [require.resolve('.')],
reportName: 'X-Pack Alerting API Integration Tests - Actions',
enableFooterInEmail: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,29 @@ export default function emailTest({ getService }: FtrProviderContext) {
}
});

it('does not have a footer', async () => {
const from = `bob@${EmailDomainAllowed}`;
const conn = await createConnector(from);
expect(conn.status).to.be(200);

const { id } = conn.body;
expect(id).to.be.a('string');

const to = EmailDomainsAllowed.map((domain) => `jeb@${domain}`).sort();
const cc = EmailDomainsAllowed.map((domain) => `jim@${domain}`).sort();
const bcc = EmailDomainsAllowed.map((domain) => `joe@${domain}`).sort();

const ccNames = cc.map((email) => `Jimmy Jack <${email}>`);

const run = await runConnector(id, to, ccNames, bcc);
expect(run.status).to.be(200);

const { status } = run.body || {};
expect(status).to.be('ok');

expect(run.body.data.message.text).to.be('email-message');
});

describe('fails for invalid email domains', () => {
it('in create when invalid "from" used', async () => {
const from = `bob@not.allowed`;
Expand Down

0 comments on commit c1a10fd

Please sign in to comment.