From bfab07caee922ebbe011cec706ee5b5545e1d30a Mon Sep 17 00:00:00 2001 From: Tianle Huang <60111637+tianleh@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:55:05 -0700 Subject: [PATCH] Pass request headers when making application config calls (#6164) Application config plugin allows external plugin to register a different storage other than the default OpenSearch. The external plugin may need some additional information about the request headers when handling the requests. * Allow extra information in the application config calls Signed-off-by: Tianle Huang * only expose headers Signed-off-by: Tianle Huang * update jsdoc Signed-off-by: Tianle Huang * add more logs Signed-off-by: Tianle Huang * update change log Signed-off-by: Tianle Huang * update readme Signed-off-by: Tianle Huang * update readme Signed-off-by: Tianle Huang --------- Signed-off-by: Tianle Huang --- CHANGELOG.md | 1 + src/plugins/application_config/README.md | 10 ++++--- .../server/routes/index.test.ts | 8 ++++-- .../application_config/server/routes/index.ts | 27 +++++++++++++++---- .../application_config/server/types.ts | 25 ++++++++++++----- .../csp_handler/server/csp_handlers.ts | 4 ++- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2baf835ec22a..6774b145aa94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Implement new home page ([#6065](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6065)) - Add sidecar service ([#5920](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5920)) - [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244)) +- [Dynamic Configurations] Pass request headers when making application config calls ([#6164](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6164)) ### 🐛 Bug Fixes diff --git a/src/plugins/application_config/README.md b/src/plugins/application_config/README.md index cad28722d63e..1637bdd9688f 100755 --- a/src/plugins/application_config/README.md +++ b/src/plugins/application_config/README.md @@ -61,15 +61,17 @@ Let's call this plugin `MyConfigurationClientPlugin`. First, this plugin will need to implement a class `MyConfigurationClient` based on interface `ConfigurationClient` defined in the `types.ts` under directory `src/plugins/application_config/server/types.ts`. Below are the functions inside the interface. ``` - getConfig(): Promise>; + getConfig(options?: ConfigurationClientOptions): Promise>; - getEntityConfig(entity: string): Promise; + getEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise; - updateEntityConfig(entity: string, newValue: string): Promise; + updateEntityConfig(entity: string, newValue: string, options?: ConfigurationClientOptions): Promise; - deleteEntityConfig(entity: string): Promise; + deleteEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise; ``` +`ConfigurationClientOptions` wraps some additional parameters including request headers. + Second, this plugin needs to declare `applicationConfig` as its dependency by adding it to `requiredPlugins` in its own `opensearch_dashboards.json`. Third, the plugin will define a new type called `AppPluginSetupDependencies` as follows in its own `types.ts`. diff --git a/src/plugins/application_config/server/routes/index.test.ts b/src/plugins/application_config/server/routes/index.test.ts index 086baa646d2b..0aa161bf560f 100644 --- a/src/plugins/application_config/server/routes/index.test.ts +++ b/src/plugins/application_config/server/routes/index.test.ts @@ -79,6 +79,8 @@ describe('application config routes', () => { getConfig: jest.fn().mockReturnValue(configurations), }; + const request = {}; + const okResponse = { statusCode: 200, }; @@ -89,7 +91,7 @@ describe('application config routes', () => { const logger = loggerMock.create(); - const returnedResponse = await handleGetConfig(client, response, logger); + const returnedResponse = await handleGetConfig(client, request, response, logger); expect(returnedResponse).toBe(okResponse); @@ -109,13 +111,15 @@ describe('application config routes', () => { }), }; + const request = {}; + const response = { customError: jest.fn().mockReturnValue(ERROR_RESPONSE), }; const logger = loggerMock.create(); - const returnedResponse = await handleGetConfig(client, response, logger); + const returnedResponse = await handleGetConfig(client, request, response, logger); expect(returnedResponse).toBe(ERROR_RESPONSE); diff --git a/src/plugins/application_config/server/routes/index.ts b/src/plugins/application_config/server/routes/index.ts index 7a059bf52f35..b6ec638e1aa9 100644 --- a/src/plugins/application_config/server/routes/index.ts +++ b/src/plugins/application_config/server/routes/index.ts @@ -26,7 +26,7 @@ export function defineRoutes( async (context, request, response) => { const client = getConfigurationClient(context.core.opensearch.client); - return await handleGetConfig(client, response, logger); + return await handleGetConfig(client, request, response, logger); } ); router.get( @@ -85,8 +85,12 @@ export async function handleGetEntityConfig( response: OpenSearchDashboardsResponseFactory, logger: Logger ) { + logger.info(`Received a request to get entity config for ${request.params.entity}.`); + try { - const result = await client.getEntityConfig(request.params.entity); + const result = await client.getEntityConfig(request.params.entity, { + headers: request.headers, + }); return response.ok({ body: { value: result, @@ -104,8 +108,14 @@ export async function handleUpdateEntityConfig( response: OpenSearchDashboardsResponseFactory, logger: Logger ) { + logger.info( + `Received a request to update entity ${request.params.entity} with new value ${request.body.newValue}.` + ); + try { - const result = await client.updateEntityConfig(request.params.entity, request.body.newValue); + const result = await client.updateEntityConfig(request.params.entity, request.body.newValue, { + headers: request.headers, + }); return response.ok({ body: { newValue: result, @@ -123,8 +133,12 @@ export async function handleDeleteEntityConfig( response: OpenSearchDashboardsResponseFactory, logger: Logger ) { + logger.info(`Received a request to delete entity ${request.params.entity}.`); + try { - const result = await client.deleteEntityConfig(request.params.entity); + const result = await client.deleteEntityConfig(request.params.entity, { + headers: request.headers, + }); return response.ok({ body: { deletedEntity: result, @@ -138,11 +152,14 @@ export async function handleDeleteEntityConfig( export async function handleGetConfig( client: ConfigurationClient, + request: OpenSearchDashboardsRequest, response: OpenSearchDashboardsResponseFactory, logger: Logger ) { + logger.info('Received a request to get all configurations.'); + try { - const result = await client.getConfig(); + const result = await client.getConfig({ headers: request.headers }); return response.ok({ body: { value: result, diff --git a/src/plugins/application_config/server/types.ts b/src/plugins/application_config/server/types.ts index 49fc11d99c53..416d0258169e 100644 --- a/src/plugins/application_config/server/types.ts +++ b/src/plugins/application_config/server/types.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { IScopedClusterClient } from 'src/core/server'; +import { IScopedClusterClient, Headers } from 'src/core/server'; export interface ApplicationConfigPluginSetup { getConfigurationClient: (inputOpenSearchClient: IScopedClusterClient) => ConfigurationClient; @@ -12,6 +12,10 @@ export interface ApplicationConfigPluginSetup { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ApplicationConfigPluginStart {} +export interface ConfigurationClientOptions { + headers: Headers; +} + /** * The interface defines the operations against the application configurations at both entity level and whole level. * @@ -20,33 +24,40 @@ export interface ConfigurationClient { /** * Get all the configurations. * - * @param {array} array of connections - * @returns {ConnectionPool} + * @param {options} options, an optional parameter + * @returns {Map} all the configurations */ - getConfig(): Promise>; + getConfig(options?: ConfigurationClientOptions): Promise>; /** * Get the value for the input entity. * * @param {entity} name of the entity + * @param {options} options, an optional parameter * @returns {string} value of the entity */ - getEntityConfig(entity: string): Promise; + getEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise; /** * Update the input entity with a new value. * * @param {entity} name of the entity * @param {newValue} new configuration value of the entity + * @param {options} options, an optional parameter * @returns {string} updated configuration value of the entity */ - updateEntityConfig(entity: string, newValue: string): Promise; + updateEntityConfig( + entity: string, + newValue: string, + options?: ConfigurationClientOptions + ): Promise; /** * Delete the input entity from configurations. * * @param {entity} name of the entity + * @param {options} options, an optional parameter * @returns {string} name of the deleted entity */ - deleteEntityConfig(entity: string): Promise; + deleteEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise; } diff --git a/src/plugins/csp_handler/server/csp_handlers.ts b/src/plugins/csp_handler/server/csp_handlers.ts index cc14da74aed5..3bfa90115518 100644 --- a/src/plugins/csp_handler/server/csp_handlers.ts +++ b/src/plugins/csp_handler/server/csp_handlers.ts @@ -51,7 +51,9 @@ export function createCspRulesPreResponseHandler( const client = getConfigurationClient(coreStart.opensearch.client.asScoped(request)); - const cspRules = await client.getEntityConfig(CSP_RULES_CONFIG_KEY); + const cspRules = await client.getEntityConfig(CSP_RULES_CONFIG_KEY, { + headers: request.headers, + }); if (!cspRules) { return appendFrameAncestorsWhenMissing(cspHeader, toolkit);