[Response Ops][Actions] Using fake request to create scoped client for ConnectorTokenClient unsecured actions usage. (#223447)

## Summary

Addresses a bug with the `ConnectorTokenClient` when using the
`UnsecuredActionClient` to execute actions directly (vs enqueuing a task
for execution). We previous pass in an internal saved objects repository
(that doesn't require a user request) to the `ConnectorTokenClient` when
using the `UnsecuredActionsClient` but this does not create the
`connector_token` saved object correctly so the next time it's read, we
get a `Failed to decrypt attribute` error.

This only occurs when using the `sendAttachmentEmail` with the MS
Exchange connector function added in this PR:
https://github.com/elastic/kibana/pull/219164. It does not affect the
other email service methods.

## To Verify

1. Ask me for MS Exchange credentials
2. Add this to your Kibana config:

```
xpack.actions.preconfigured:
  test-exchange-email:
    name: preconfigured-exchange-email
    actionTypeId: .email
    config:
      service: exchange_server
      clientId: <clientId>
      tenantId: <tenantId>
      from: <from>
    secrets:
      clientSecret: <secret>
notifications.connectors.default.email: test-exchange-email
```
3. Make this change to the code so Kibana sends 2 emails when it starts
up:

```
--- a/x-pack/platform/plugins/shared/notifications/server/plugin.ts
+++ b/x-pack/platform/plugins/shared/notifications/server/plugin.ts
@@ -40,6 +40,27 @@ export class NotificationsPlugin
   public start(_core: CoreStart, plugins: NotificationsServerStartDependencies) {
     const emailStartContract = this.emailServiceProvider.start(plugins);

+    const emailService = emailStartContract.getEmailService();
+    emailService
+      .sendAttachmentEmail({
+        to: ['<email>'],
+        subject: 'yo',
+        message: 'i am here',
+        attachments: [],
+        spaceId: 'default',
+      })
+      .then(() => {
+        new Promise((resolve) => setTimeout(resolve, 5000)).then(() => {
+          emailService.sendAttachmentEmail({
+            to: ['<email>'],
+            subject: 'yo',
+            message: 'i am here again',
+            attachments: [],
+            spaceId: 'default',
+          });
+        });
+      });
+
     return {
```

4. Verify there are no decryption errors for the `connector_token` SO
logged and that the emails are sent successfully.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Ying Mao 2025-06-23 13:31:34 -04:00 committed by GitHub
parent c868136f48
commit 3af496d11a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -558,7 +558,7 @@ export class ActionsPlugin
getInternalSavedObjectsRepositoryWithoutAccessToActions, getInternalSavedObjectsRepositoryWithoutAccessToActions,
core.elasticsearch, core.elasticsearch,
encryptedSavedObjectsClient, encryptedSavedObjectsClient,
() => core.savedObjects.createInternalRepository(includedHiddenTypes) () => this.getUnsecuredSavedObjectsClientWithFakeRequest(core.savedObjects)
), ),
encryptedSavedObjectsClient, encryptedSavedObjectsClient,
actionTypeRegistry: actionTypeRegistry!, actionTypeRegistry: actionTypeRegistry!,
@ -635,6 +635,24 @@ export class ActionsPlugin
includedHiddenTypes, includedHiddenTypes,
}); });
// replace when https://github.com/elastic/kibana/issues/209413 is resolved
private getUnsecuredSavedObjectsClientWithFakeRequest = (
savedObjects: CoreStart['savedObjects']
) => {
const fakeRequest = {
headers: {},
getBasePath: () => '',
path: '/',
route: { settings: {} },
url: { href: {} },
raw: { req: { url: '/' } },
} as unknown as KibanaRequest;
return savedObjects.getScopedClient(fakeRequest, {
excludedExtensions: [SECURITY_EXTENSION_ID],
includedHiddenTypes,
});
};
private instantiateAuthorization = (request: KibanaRequest) => { private instantiateAuthorization = (request: KibanaRequest) => {
return new ActionsAuthorization({ return new ActionsAuthorization({
request, request,
@ -665,7 +683,7 @@ export class ActionsPlugin
getSavedObjectRepository: () => ISavedObjectsRepository, getSavedObjectRepository: () => ISavedObjectsRepository,
elasticsearch: ElasticsearchServiceStart, elasticsearch: ElasticsearchServiceStart,
encryptedSavedObjectsClient: EncryptedSavedObjectsClient, encryptedSavedObjectsClient: EncryptedSavedObjectsClient,
unsecuredSavedObjectsRepository: () => ISavedObjectsRepository unsecuredSavedObjectsRepository: () => SavedObjectsClientContract
): () => UnsecuredServices { ): () => UnsecuredServices {
return () => { return () => {
return { return {