mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Response Ops][Actions] Adding configuration to override default MS Graph API Scope and Exchange URL values (#175812)
Resolves https://github.com/elastic/kibana/issues/166064 ## Summary Adds the following configurations to the `kibana.yml` config: * `xpack.actions.microsoftGraphApiScope` - overrides the default Graph API scope value of `https://graph.microsoft.com/.default` * `xpack.actions.microsoftExchangeUrl` - overrides the default value of `https://login.microsoftonline.com` This allows users in different Azure environments to customize their endpoints as needed. ## To Verify We are unable to test this in a different environment but we can verify that the config overrides the defaults as expected by setting the config values to something different and the logging out the params that are sent to `getOAuthClientCredentialsAccessToken` in `x-pack/plugins/stack_connectors/server/connector_types/email/send_email.ts`. Then create an MS Exchange email connector and test it to see that the logged values are overridden as expected. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
859e440eb9
commit
f7e4f7a636
16 changed files with 205 additions and 23 deletions
|
@ -142,6 +142,15 @@ A list of action types that are enabled. It defaults to `["*"]`, enabling all ty
|
|||
+
|
||||
Disabled action types will not appear as an option when creating new connectors, but existing connectors and actions of that type will remain in {kib} and will not function.
|
||||
|
||||
`xpack.actions.microsoftExchangeUrl`::
|
||||
The URL for the Microsoft Azure Active Directory endpoint to use for MS Exchange email authentication. Default: `https://login.microsoftonline.com`.
|
||||
|
||||
`xpack.actions.microsoftGraphApiUrl`::
|
||||
The URL for the Microsoft Graph API endpoint to use for MS Exchange email authentication. Default: `https://graph.microsoft.com/v1.0`.
|
||||
|
||||
`xpack.actions.microsoftGraphApiScope`::
|
||||
The URL for the Microsoft Graph API scope endpoint to use for MS Exchange email authentication. Default: `https://graph.microsoft.com/.default`.
|
||||
|
||||
`xpack.actions.proxyUrl` {ess-icon}::
|
||||
Specifies the proxy URL to use, if using a proxy for actions. By default, no proxy is used.
|
||||
+
|
||||
|
|
|
@ -19,3 +19,7 @@ export * from './execution_log_types';
|
|||
export const BASE_ACTION_API_PATH = '/api/actions';
|
||||
export const INTERNAL_BASE_ACTION_API_PATH = '/internal/actions';
|
||||
export const ACTIONS_FEATURE_ID = 'actions';
|
||||
|
||||
export const DEFAULT_MICROSOFT_EXCHANGE_URL = 'https://login.microsoftonline.com';
|
||||
export const DEFAULT_MICROSOFT_GRAPH_API_URL = 'https://graph.microsoft.com/v1.0';
|
||||
export const DEFAULT_MICROSOFT_GRAPH_API_SCOPE = 'https://graph.microsoft.com/.default';
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import moment from 'moment';
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
|
||||
import {
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../../common';
|
||||
import { ActionTypeRegistry, ActionTypeRegistryOpts } from '../action_type_registry';
|
||||
import { ActionsClient } from './actions_client';
|
||||
import { ExecutorType, ActionType } from '../types';
|
||||
|
@ -596,6 +600,9 @@ describe('create()', () => {
|
|||
proxyVerificationMode: 'full',
|
||||
},
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
});
|
||||
|
||||
const localActionTypeRegistryParams = {
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../common';
|
||||
import { ActionsConfigurationUtilities } from './actions_config';
|
||||
|
||||
const createActionsConfigMock = () => {
|
||||
|
@ -24,7 +29,9 @@ const createActionsConfigMock = () => {
|
|||
timeout: 360000,
|
||||
}),
|
||||
getCustomHostSettings: jest.fn().mockReturnValue(undefined),
|
||||
getMicrosoftGraphApiUrl: jest.fn().mockReturnValue(undefined),
|
||||
getMicrosoftGraphApiUrl: jest.fn().mockReturnValue(DEFAULT_MICROSOFT_GRAPH_API_URL),
|
||||
getMicrosoftGraphApiScope: jest.fn().mockReturnValue(DEFAULT_MICROSOFT_GRAPH_API_SCOPE),
|
||||
getMicrosoftExchangeUrl: jest.fn().mockReturnValue(DEFAULT_MICROSOFT_EXCHANGE_URL),
|
||||
validateEmailAddresses: jest.fn().mockReturnValue(undefined),
|
||||
getMaxAttempts: jest.fn().mockReturnValue(3),
|
||||
enableFooterInEmail: jest.fn().mockReturnValue(true),
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { ActionsConfig } from './config';
|
||||
import {
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../common';
|
||||
import {
|
||||
getActionsConfigurationUtilities,
|
||||
AllowedHosts,
|
||||
|
@ -34,6 +39,9 @@ const defaultActionsConfig: ActionsConfig = {
|
|||
verificationMode: 'full',
|
||||
},
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
};
|
||||
|
||||
describe('ensureUriAllowed', () => {
|
||||
|
|
|
@ -47,7 +47,9 @@ export interface ActionsConfigurationUtilities {
|
|||
getProxySettings: () => undefined | ProxySettings;
|
||||
getResponseSettings: () => ResponseSettings;
|
||||
getCustomHostSettings: (targetUrl: string) => CustomHostSettings | undefined;
|
||||
getMicrosoftGraphApiUrl: () => undefined | string;
|
||||
getMicrosoftGraphApiUrl: () => string;
|
||||
getMicrosoftGraphApiScope: () => string;
|
||||
getMicrosoftExchangeUrl: () => string;
|
||||
getMaxAttempts: ({
|
||||
actionTypeMaxAttempts,
|
||||
actionTypeId,
|
||||
|
@ -127,10 +129,18 @@ function getProxySettingsFromConfig(config: ActionsConfig): undefined | ProxySet
|
|||
};
|
||||
}
|
||||
|
||||
function getMicrosoftGraphApiUrlFromConfig(config: ActionsConfig): undefined | string {
|
||||
function getMicrosoftGraphApiUrlFromConfig(config: ActionsConfig): string {
|
||||
return config.microsoftGraphApiUrl;
|
||||
}
|
||||
|
||||
function getMicrosoftGraphApiScopeFromConfig(config: ActionsConfig): string {
|
||||
return config.microsoftGraphApiScope;
|
||||
}
|
||||
|
||||
function getMicrosoftExchangeUrlFromConfig(config: ActionsConfig): string {
|
||||
return config.microsoftExchangeUrl;
|
||||
}
|
||||
|
||||
function arrayAsSet<T>(arr: T[] | undefined): Set<T> | undefined {
|
||||
if (!arr) return;
|
||||
return new Set(arr);
|
||||
|
@ -209,6 +219,8 @@ export function getActionsConfigurationUtilities(
|
|||
},
|
||||
getCustomHostSettings: (targetUrl: string) => getCustomHostSettings(config, targetUrl),
|
||||
getMicrosoftGraphApiUrl: () => getMicrosoftGraphApiUrlFromConfig(config),
|
||||
getMicrosoftGraphApiScope: () => getMicrosoftGraphApiScopeFromConfig(config),
|
||||
getMicrosoftExchangeUrl: () => getMicrosoftExchangeUrlFromConfig(config),
|
||||
validateEmailAddresses: (addresses: string[], options: ValidateEmailAddressesOptions) =>
|
||||
validatedEmailCurried(addresses, options),
|
||||
getMaxAttempts: ({ actionTypeMaxAttempts, actionTypeId }) => {
|
||||
|
|
|
@ -30,6 +30,9 @@ describe('config validation', () => {
|
|||
"maxResponseContentLength": ByteSizeValue {
|
||||
"valueInBytes": 1048576,
|
||||
},
|
||||
"microsoftExchangeUrl": "https://login.microsoftonline.com",
|
||||
"microsoftGraphApiScope": "https://graph.microsoft.com/.default",
|
||||
"microsoftGraphApiUrl": "https://graph.microsoft.com/v1.0",
|
||||
"preconfigured": Object {},
|
||||
"preconfiguredAlertHistoryEsIndex": false,
|
||||
"proxyRejectUnauthorizedCertificates": true,
|
||||
|
@ -65,6 +68,9 @@ describe('config validation', () => {
|
|||
"maxResponseContentLength": ByteSizeValue {
|
||||
"valueInBytes": 1048576,
|
||||
},
|
||||
"microsoftExchangeUrl": "https://login.microsoftonline.com",
|
||||
"microsoftGraphApiScope": "https://graph.microsoft.com/.default",
|
||||
"microsoftGraphApiUrl": "https://graph.microsoft.com/v1.0",
|
||||
"preconfigured": Object {
|
||||
"mySlack1": Object {
|
||||
"actionTypeId": ".slack",
|
||||
|
@ -147,7 +153,7 @@ describe('config validation', () => {
|
|||
expect(mockLogger.warn.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"The confgurations xpack.actions.proxyBypassHosts and xpack.actions.proxyOnlyHosts can not be used at the same time. The configuration xpack.actions.proxyOnlyHosts will be ignored.",
|
||||
"The configurations xpack.actions.proxyBypassHosts and xpack.actions.proxyOnlyHosts can not be used at the same time. The configuration xpack.actions.proxyOnlyHosts will be ignored.",
|
||||
],
|
||||
]
|
||||
`);
|
||||
|
@ -169,7 +175,7 @@ describe('config validation', () => {
|
|||
expect(mockLogger.warn.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"The confguration xpack.actions.proxyUrl: bad url is invalid.",
|
||||
"The configuration xpack.actions.proxyUrl: bad url is invalid.",
|
||||
],
|
||||
]
|
||||
`);
|
||||
|
@ -207,6 +213,9 @@ describe('config validation', () => {
|
|||
"maxResponseContentLength": ByteSizeValue {
|
||||
"valueInBytes": 1048576,
|
||||
},
|
||||
"microsoftExchangeUrl": "https://login.microsoftonline.com",
|
||||
"microsoftGraphApiScope": "https://graph.microsoft.com/.default",
|
||||
"microsoftGraphApiUrl": "https://graph.microsoft.com/v1.0",
|
||||
"preconfigured": Object {},
|
||||
"preconfiguredAlertHistoryEsIndex": false,
|
||||
"proxyRejectUnauthorizedCertificates": true,
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { Logger } from '@kbn/core/server';
|
||||
import {
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../common';
|
||||
|
||||
export enum AllowedHosts {
|
||||
Any = '*',
|
||||
|
@ -120,7 +125,9 @@ export const configSchema = schema.object({
|
|||
maxResponseContentLength: schema.byteSize({ defaultValue: '1mb' }),
|
||||
responseTimeout: schema.duration({ defaultValue: '60s' }),
|
||||
customHostSettings: schema.maybe(schema.arrayOf(customHostSettingsSchema)),
|
||||
microsoftGraphApiUrl: schema.maybe(schema.string()),
|
||||
microsoftGraphApiUrl: schema.string({ defaultValue: DEFAULT_MICROSOFT_GRAPH_API_URL }),
|
||||
microsoftGraphApiScope: schema.string({ defaultValue: DEFAULT_MICROSOFT_GRAPH_API_SCOPE }),
|
||||
microsoftExchangeUrl: schema.string({ defaultValue: DEFAULT_MICROSOFT_EXCHANGE_URL }),
|
||||
email: schema.maybe(
|
||||
schema.object({
|
||||
domain_allowlist: schema.arrayOf(schema.string()),
|
||||
|
@ -155,13 +162,13 @@ export function getValidatedConfig(logger: Logger, originalConfig: ActionsConfig
|
|||
try {
|
||||
new URL(proxyUrl);
|
||||
} catch (err) {
|
||||
logger.warn(`The confguration xpack.actions.proxyUrl: ${proxyUrl} is invalid.`);
|
||||
logger.warn(`The configuration xpack.actions.proxyUrl: ${proxyUrl} is invalid.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (proxyBypassHosts && proxyOnlyHosts) {
|
||||
logger.warn(
|
||||
'The confgurations xpack.actions.proxyBypassHosts and xpack.actions.proxyOnlyHosts can not be used at the same time. The configuration xpack.actions.proxyOnlyHosts will be ignored.'
|
||||
'The configurations xpack.actions.proxyBypassHosts and xpack.actions.proxyOnlyHosts can not be used at the same time. The configuration xpack.actions.proxyOnlyHosts will be ignored.'
|
||||
);
|
||||
const tmp: Record<string, unknown> = originalConfig;
|
||||
delete tmp.proxyOnlyHosts;
|
||||
|
|
|
@ -23,6 +23,11 @@ import { createReadySignal } from '@kbn/event-log-plugin/server/lib/ready_signal
|
|||
import { ActionsConfig } from '../config';
|
||||
import { ActionsConfigurationUtilities, getActionsConfigurationUtilities } from '../actions_config';
|
||||
import { resolveCustomHosts } from '../lib/custom_host_settings';
|
||||
import {
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../../common';
|
||||
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
|
@ -683,6 +688,9 @@ const BaseActionsConfig: ActionsConfig = {
|
|||
responseTimeout: momentDuration(1000 * 30),
|
||||
customHostSettings: undefined,
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
};
|
||||
|
||||
function getACUfromConfig(config: Partial<ActionsConfig> = {}): ActionsConfigurationUtilities {
|
||||
|
|
|
@ -23,6 +23,11 @@ import { createReadySignal } from '@kbn/event-log-plugin/server/lib/ready_signal
|
|||
import { ActionsConfig } from '../config';
|
||||
import { ActionsConfigurationUtilities, getActionsConfigurationUtilities } from '../actions_config';
|
||||
import { resolveCustomHosts } from '../lib/custom_host_settings';
|
||||
import {
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
} from '../../common';
|
||||
|
||||
const logger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
|
@ -589,6 +594,9 @@ const BaseActionsConfig: ActionsConfig = {
|
|||
responseTimeout: momentDuration(1000 * 30),
|
||||
customHostSettings: undefined,
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
};
|
||||
|
||||
function getACUfromConfig(config: Partial<ActionsConfig> = {}): ActionsConfigurationUtilities {
|
||||
|
|
|
@ -15,6 +15,11 @@ import { Logger } from '@kbn/core/server';
|
|||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
|
||||
import { resolveCustomHosts, getCanonicalCustomHostUrl } from './custom_host_settings';
|
||||
import {
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
} from '../../common';
|
||||
|
||||
const CA_DIR = '../../../../../../packages/kbn-dev-utils/certs';
|
||||
const CA_FILE1 = pathResolve(__filename, pathJoin(CA_DIR, 'ca.crt'));
|
||||
|
@ -74,6 +79,9 @@ describe('custom_host_settings', () => {
|
|||
maxResponseContentLength: new ByteSizeValue(1000000),
|
||||
responseTimeout: moment.duration(60000),
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
};
|
||||
|
||||
test('ensure it copies over the config parts that it does not touch', () => {
|
||||
|
|
|
@ -24,7 +24,12 @@ import {
|
|||
ActionsPluginsStart,
|
||||
PluginSetupContract,
|
||||
} from './plugin';
|
||||
import { AlertHistoryEsIndexConnectorId } from '../common';
|
||||
import {
|
||||
AlertHistoryEsIndexConnectorId,
|
||||
DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
} from '../common';
|
||||
|
||||
const executor: ExecutorType<{}, {}, {}, void> = async (options) => {
|
||||
return { status: 'ok', actionId: options.actionId };
|
||||
|
@ -51,6 +56,9 @@ function getConfig(overrides = {}) {
|
|||
maxResponseContentLength: new ByteSizeValue(1000000),
|
||||
responseTimeout: moment.duration('60s'),
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
@ -73,6 +81,9 @@ describe('Actions Plugin', () => {
|
|||
maxResponseContentLength: new ByteSizeValue(1000000),
|
||||
responseTimeout: moment.duration(60000),
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
});
|
||||
plugin = new ActionsPlugin(context);
|
||||
coreSetup = coreMock.createSetup();
|
||||
|
@ -534,6 +545,9 @@ describe('Actions Plugin', () => {
|
|||
maxResponseContentLength: new ByteSizeValue(1000000),
|
||||
responseTimeout: moment.duration(60000),
|
||||
enableFooterInEmail: true,
|
||||
microsoftGraphApiUrl: DEFAULT_MICROSOFT_GRAPH_API_URL,
|
||||
microsoftGraphApiScope: DEFAULT_MICROSOFT_GRAPH_API_SCOPE,
|
||||
microsoftExchangeUrl: DEFAULT_MICROSOFT_EXCHANGE_URL,
|
||||
});
|
||||
plugin = new ActionsPlugin(context);
|
||||
coreSetup = coreMock.createSetup();
|
||||
|
|
|
@ -179,7 +179,6 @@ describe('send_email module', () => {
|
|||
expect(sendEmailGraphApiMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"graphApiUrl": undefined,
|
||||
"headers": Object {
|
||||
"Authorization": "Bearer dfjsdfgdjhfgsjdf",
|
||||
"Content-Type": "application/json",
|
||||
|
@ -232,6 +231,82 @@ describe('send_email module', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('uses custom graph API scope if configured for OAuth 2.0 Client Credentials authentication for email using "exchange_server" service', async () => {
|
||||
const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock;
|
||||
const getOAuthClientCredentialsAccessTokenMock =
|
||||
getOAuthClientCredentialsAccessToken as jest.Mock;
|
||||
const sendEmailOptions = getSendEmailOptions({
|
||||
transport: {
|
||||
service: 'exchange_server',
|
||||
clientId: '123456',
|
||||
tenantId: '98765',
|
||||
clientSecret: 'sdfhkdsjhfksdjfh',
|
||||
},
|
||||
});
|
||||
sendEmailOptions.configurationUtilities.getMicrosoftGraphApiScope.mockReturnValueOnce(
|
||||
'https://dod-graph.microsoft.us/.default'
|
||||
);
|
||||
getOAuthClientCredentialsAccessTokenMock.mockReturnValueOnce(`Bearer dfjsdfgdjhfgsjdf`);
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 5);
|
||||
|
||||
sendEmailGraphApiMock.mockReturnValue({
|
||||
status: 202,
|
||||
});
|
||||
|
||||
await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient);
|
||||
expect(getOAuthClientCredentialsAccessTokenMock).toHaveBeenCalledWith({
|
||||
configurationUtilities: sendEmailOptions.configurationUtilities,
|
||||
connectorId: '1',
|
||||
connectorTokenClient,
|
||||
credentials: {
|
||||
config: { clientId: '123456', tenantId: '98765' },
|
||||
secrets: { clientSecret: 'sdfhkdsjhfksdjfh' },
|
||||
},
|
||||
logger: mockLogger,
|
||||
oAuthScope: 'https://dod-graph.microsoft.us/.default',
|
||||
tokenUrl: 'https://login.microsoftonline.com/98765/oauth2/v2.0/token',
|
||||
});
|
||||
});
|
||||
|
||||
test('uses custom exchange URL if configured for OAuth 2.0 Client Credentials authentication for email using "exchange_server" service', async () => {
|
||||
const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock;
|
||||
const getOAuthClientCredentialsAccessTokenMock =
|
||||
getOAuthClientCredentialsAccessToken as jest.Mock;
|
||||
const sendEmailOptions = getSendEmailOptions({
|
||||
transport: {
|
||||
service: 'exchange_server',
|
||||
clientId: '123456',
|
||||
tenantId: '98765',
|
||||
clientSecret: 'sdfhkdsjhfksdjfh',
|
||||
},
|
||||
});
|
||||
sendEmailOptions.configurationUtilities.getMicrosoftExchangeUrl.mockReturnValueOnce(
|
||||
'https://login.microsoftonline.us'
|
||||
);
|
||||
getOAuthClientCredentialsAccessTokenMock.mockReturnValueOnce(`Bearer dfjsdfgdjhfgsjdf`);
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 5);
|
||||
|
||||
sendEmailGraphApiMock.mockReturnValue({
|
||||
status: 202,
|
||||
});
|
||||
|
||||
await sendEmail(mockLogger, sendEmailOptions, connectorTokenClient);
|
||||
expect(getOAuthClientCredentialsAccessTokenMock).toHaveBeenCalledWith({
|
||||
configurationUtilities: sendEmailOptions.configurationUtilities,
|
||||
connectorId: '1',
|
||||
connectorTokenClient,
|
||||
credentials: {
|
||||
config: { clientId: '123456', tenantId: '98765' },
|
||||
secrets: { clientSecret: 'sdfhkdsjhfksdjfh' },
|
||||
},
|
||||
logger: mockLogger,
|
||||
oAuthScope: 'https://graph.microsoft.com/.default',
|
||||
tokenUrl: 'https://login.microsoftonline.us/98765/oauth2/v2.0/token',
|
||||
});
|
||||
});
|
||||
|
||||
test('throws error if null access token returned when using OAuth 2.0 Client Credentials authentication', async () => {
|
||||
const sendEmailGraphApiMock = sendEmailGraphApi as jest.Mock;
|
||||
const getOAuthClientCredentialsAccessTokenMock =
|
||||
|
|
|
@ -25,8 +25,6 @@ import { sendEmailGraphApi } from './send_email_graph_api';
|
|||
// an email "service" which doesn't actually send, just returns what it would send
|
||||
export const JSON_TRANSPORT_SERVICE = '__json';
|
||||
// The value is the resource identifier (Application ID URI) of the resource you want, affixed with the .default suffix. For Microsoft Graph, the value is https://graph.microsoft.com/.default. This value informs the Microsoft identity platform endpoint that of all the application permissions you have configured for your app in the app registration portal, it should issue a token for the ones associated with the resource you want to use.
|
||||
export const GRAPH_API_OAUTH_SCOPE = 'https://graph.microsoft.com/.default';
|
||||
export const EXCHANGE_ONLINE_SERVER_HOST = 'https://login.microsoftonline.com';
|
||||
|
||||
export interface SendEmailOptions {
|
||||
connectorId: string;
|
||||
|
@ -92,6 +90,12 @@ export async function sendEmailWithExchange(
|
|||
const { transport, configurationUtilities, connectorId } = options;
|
||||
const { clientId, clientSecret, tenantId, oauthTokenUrl } = transport;
|
||||
|
||||
let tokenUrl = oauthTokenUrl;
|
||||
if (!tokenUrl) {
|
||||
const exchangeUrl = configurationUtilities.getMicrosoftExchangeUrl();
|
||||
tokenUrl = `${exchangeUrl}/${tenantId}/oauth2/v2.0/token`;
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthClientCredentialsAccessToken({
|
||||
connectorId,
|
||||
logger,
|
||||
|
@ -105,8 +109,8 @@ export async function sendEmailWithExchange(
|
|||
clientSecret: clientSecret as string,
|
||||
},
|
||||
},
|
||||
oAuthScope: GRAPH_API_OAUTH_SCOPE,
|
||||
tokenUrl: oauthTokenUrl ?? `${EXCHANGE_ONLINE_SERVER_HOST}/${tenantId}/oauth2/v2.0/token`,
|
||||
oAuthScope: configurationUtilities.getMicrosoftGraphApiScope(),
|
||||
tokenUrl,
|
||||
connectorTokenClient,
|
||||
});
|
||||
|
||||
|
@ -148,7 +152,6 @@ export async function sendEmailWithExchange(
|
|||
options,
|
||||
headers,
|
||||
messageHTML,
|
||||
graphApiUrl: configurationUtilities.getMicrosoftGraphApiUrl(),
|
||||
},
|
||||
logger,
|
||||
configurationUtilities,
|
||||
|
|
|
@ -32,7 +32,11 @@ describe('sendEmailGraphApi', () => {
|
|||
status: 202,
|
||||
});
|
||||
await sendEmailGraphApi(
|
||||
{ options: getSendEmailOptions(), messageHTML: 'test1', headers: {} },
|
||||
{
|
||||
options: getSendEmailOptions(),
|
||||
messageHTML: 'test1',
|
||||
headers: {},
|
||||
},
|
||||
logger,
|
||||
configurationUtilities
|
||||
);
|
||||
|
@ -207,12 +211,12 @@ describe('sendEmailGraphApi', () => {
|
|||
axiosInstanceMock.mockReturnValueOnce({
|
||||
status: 202,
|
||||
});
|
||||
configurationUtilities.getMicrosoftGraphApiUrl.mockReturnValueOnce('https://test');
|
||||
await sendEmailGraphApi(
|
||||
{
|
||||
options: getSendEmailOptions(),
|
||||
messageHTML: 'test3',
|
||||
headers: {},
|
||||
graphApiUrl: 'https://test',
|
||||
},
|
||||
logger,
|
||||
configurationUtilities
|
||||
|
|
|
@ -17,18 +17,15 @@ interface SendEmailGraphApiOptions {
|
|||
options: SendEmailOptions;
|
||||
headers: Record<string, string>;
|
||||
messageHTML: string;
|
||||
graphApiUrl?: string;
|
||||
}
|
||||
|
||||
const MICROSOFT_GRAPH_API_HOST = 'https://graph.microsoft.com/v1.0';
|
||||
|
||||
export async function sendEmailGraphApi(
|
||||
sendEmailOptions: SendEmailGraphApiOptions,
|
||||
logger: Logger,
|
||||
configurationUtilities: ActionsConfigurationUtilities,
|
||||
axiosInstance?: AxiosInstance
|
||||
): Promise<AxiosResponse> {
|
||||
const { options, headers, messageHTML, graphApiUrl } = sendEmailOptions;
|
||||
const { options, headers, messageHTML } = sendEmailOptions;
|
||||
|
||||
// Create a new axios instance if one is not provided
|
||||
axiosInstance = axiosInstance ?? axios.create();
|
||||
|
@ -36,7 +33,9 @@ export async function sendEmailGraphApi(
|
|||
// POST /users/{id | userPrincipalName}/sendMail
|
||||
const res = await request({
|
||||
axios: axiosInstance,
|
||||
url: `${graphApiUrl ?? MICROSOFT_GRAPH_API_HOST}/users/${options.routing.from}/sendMail`,
|
||||
url: `${configurationUtilities.getMicrosoftGraphApiUrl()}/users/${
|
||||
options.routing.from
|
||||
}/sendMail`,
|
||||
method: 'post',
|
||||
logger,
|
||||
data: getMessage(options, messageHTML),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue