Skip to content

Commit

Permalink
Refactor getClient and getLegacyClient to use authentication method r…
Browse files Browse the repository at this point in the history
…egistry

Signed-off-by: Bandini Bhopi <bandinib@amazon.com>
  • Loading branch information
bandinib-amzn committed Feb 22, 2024
1 parent 352d304 commit 71248cc
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/plugins/data_source/common/data_sources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface DataSourceAttributes extends SavedObjectAttributes {
credentials: UsernamePasswordTypedContent | SigV4Content | undefined | AuthTypeContent;
};
lastUpdatedTime?: string;
name: AuthType | string;
}

export interface AuthTypeContent {
Expand All @@ -30,6 +31,7 @@ export interface SigV4Content extends SavedObjectAttributes {
secretKey: string;
region: string;
service?: SigV4ServiceName;
sessionToken?: string;
}

export interface UsernamePasswordTypedContent extends SavedObjectAttributes {
Expand Down
48 changes: 38 additions & 10 deletions src/plugins/data_source/server/client/configure_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Client, ClientOptions } from '@opensearch-project/opensearch';
import { Client as LegacyClient } from 'elasticsearch';
import { Credentials } from 'aws-sdk';
import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';
import { Logger } from '../../../../../src/core/server';
import { Logger, OpenSearchDashboardsRequest } from '../../../../../src/core/server';
import {
AuthType,
DataSourceAttributes,
Expand All @@ -27,6 +27,8 @@ import {
getDataSource,
generateCacheKey,
} from './configure_client_utils';
import { IAuthenticationMethodRegistery } from '../auth_registry';
import { authRegistryCredentialProvider } from '../util/credential_provider';

export const configureClient = async (
{
Expand All @@ -35,6 +37,8 @@ export const configureClient = async (
cryptography,
testClientDataSourceAttr,
customApiSchemaRegistryPromise,
request,
authRegistry,
}: DataSourceClientParams,
openSearchClientPoolSetup: OpenSearchClientPoolSetup,
config: DataSourcePluginConfigType,
Expand Down Expand Up @@ -80,6 +84,8 @@ export const configureClient = async (
cryptography,
rootClient,
dataSourceId,
request,
authRegistry,
requireDecryption
);
} catch (error: any) {
Expand All @@ -101,6 +107,8 @@ export const configureClient = async (
* @param config data source config
* @param addClientToPool function to add client to client pool
* @param dataSourceId id of data source saved Object
* @param request OpenSearch Dashboards incoming request to read client parameters from header.
* @param authRegistry registry to retrieve the credentials provider for the authentication method in order to return the client
* @param requireDecryption false when creating test client before data source exists
* @returns Promise of query client
*/
Expand All @@ -112,15 +120,31 @@ const getQueryClient = async (
cryptography?: CryptographyServiceSetup,
rootClient?: Client,
dataSourceId?: string,
request?: OpenSearchDashboardsRequest,
authRegistry?: IAuthenticationMethodRegistery,
requireDecryption: boolean = true
): Promise<Client> => {
const {
let credential;
let {
auth: { type },
endpoint,
name,
} = dataSourceAttr;
const { endpoint } = dataSourceAttr;
name = name ?? type;
const clientOptions = parseClientOptions(config, endpoint, registeredSchema);
const cacheKey = generateCacheKey(dataSourceAttr, dataSourceId);

const authenticationMethod = authRegistry?.getAuthenticationMethod(name);
if (authenticationMethod !== undefined) {
const credentialProvider = await authRegistryCredentialProvider(authenticationMethod, {
dataSourceAttr,
request,
cryptography,
});
credential = credentialProvider.credential;
type = credentialProvider.type;
}

switch (type) {
case AuthType.NoAuth:
if (!rootClient) rootClient = new Client(clientOptions);
Expand All @@ -129,21 +153,25 @@ const getQueryClient = async (
return rootClient.child();

case AuthType.UsernamePasswordType:
const credential = requireDecryption
? await getCredential(dataSourceAttr, cryptography!)
: (dataSourceAttr.auth.credentials as UsernamePasswordTypedContent);
credential =
(credential as UsernamePasswordTypedContent) ??
(requireDecryption
? await getCredential(dataSourceAttr, cryptography!)
: (dataSourceAttr.auth.credentials as UsernamePasswordTypedContent));

if (!rootClient) rootClient = new Client(clientOptions);
addClientToPool(cacheKey, type, rootClient);

return getBasicAuthClient(rootClient, credential);

case AuthType.SigV4:
const awsCredential = requireDecryption
? await getAWSCredential(dataSourceAttr, cryptography!)
: (dataSourceAttr.auth.credentials as SigV4Content);
credential =
(credential as SigV4Content) ??
(requireDecryption
? await getAWSCredential(dataSourceAttr, cryptography!)
: (dataSourceAttr.auth.credentials as SigV4Content));

const awsClient = rootClient ? rootClient : getAWSClient(awsCredential, clientOptions);
const awsClient = rootClient ? rootClient : getAWSClient(credential, clientOptions);
addClientToPool(cacheKey, type, awsClient);

return awsClient;
Expand Down
40 changes: 33 additions & 7 deletions src/plugins/data_source/server/legacy/configure_legacy_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
LegacyCallAPIOptions,
LegacyOpenSearchErrorHelpers,
Logger,
OpenSearchDashboardsRequest,
} from '../../../../../src/core/server';
import {
AuthType,
Expand All @@ -34,13 +35,17 @@ import {
getDataSource,
generateCacheKey,
} from '../client/configure_client_utils';
import { IAuthenticationMethodRegistery } from '../auth_registry';
import { authRegistryCredentialProvider } from '../util/credential_provider';

export const configureLegacyClient = async (
{
dataSourceId,
savedObjects,
cryptography,
customApiSchemaRegistryPromise,
request,
authRegistry,
}: DataSourceClientParams,
callApiParams: LegacyClientCallAPIParams,
openSearchClientPoolSetup: OpenSearchClientPoolSetup,
Expand All @@ -65,7 +70,9 @@ export const configureLegacyClient = async (
config,
registeredSchema,
rootClient,
dataSourceId
dataSourceId,
request,
authRegistry
);
} catch (error: any) {
logger.debug(
Expand Down Expand Up @@ -96,15 +103,31 @@ const getQueryClient = async (
config: DataSourcePluginConfigType,
registeredSchema: any[],
rootClient?: LegacyClient,
dataSourceId?: string
dataSourceId?: string,
request?: OpenSearchDashboardsRequest,
authRegistry?: IAuthenticationMethodRegistery
) => {
const {
let credential;
let {
auth: { type },
endpoint: nodeUrl,
name,
} = dataSourceAttr;
const { endpoint: nodeUrl } = dataSourceAttr;
name = name ?? type;
const clientOptions = parseClientOptions(config, nodeUrl, registeredSchema);
const cacheKey = generateCacheKey(dataSourceAttr, dataSourceId);

const authenticationMethod = authRegistry?.getAuthenticationMethod(name);
if (authenticationMethod !== undefined) {
const credentialProvider = await authRegistryCredentialProvider(authenticationMethod, {
dataSourceAttr,
request,
cryptography,
});
credential = credentialProvider.credential;
type = credentialProvider.type;
}

switch (type) {
case AuthType.NoAuth:
if (!rootClient) rootClient = new LegacyClient(clientOptions);
Expand All @@ -117,17 +140,20 @@ const getQueryClient = async (
);

case AuthType.UsernamePasswordType:
const credential = await getCredential(dataSourceAttr, cryptography);
credential =
(credential as UsernamePasswordTypedContent) ??
(await getCredential(dataSourceAttr, cryptography));

if (!rootClient) rootClient = new LegacyClient(clientOptions);
addClientToPool(cacheKey, type, rootClient);

return getBasicAuthClient(rootClient, { endpoint, clientParams, options }, credential);

case AuthType.SigV4:
const awsCredential = await getAWSCredential(dataSourceAttr, cryptography);
credential =
(credential as SigV4Content) ?? (await getAWSCredential(dataSourceAttr, cryptography));

const awsClient = rootClient ? rootClient : getAWSClient(awsCredential, clientOptions);
const awsClient = rootClient ? rootClient : getAWSClient(credential, clientOptions);
addClientToPool(cacheKey, type, awsClient);

return await (callAPI.bind(null, awsClient) as LegacyAPICaller)(
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/data_source/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
authRegistryPromise: Promise<IAuthenticationMethodRegistery>,
customApiSchemaRegistryPromise: Promise<CustomApiSchemaRegistry>
): IContextProvider<RequestHandler<unknown, unknown, unknown>, 'dataSource'> => {
return (context, req) => {
return async (context, req) => {
const authRegistry = await authRegistryPromise;
return {
opensearch: {
getClient: (dataSourceId: string) => {
Expand All @@ -181,6 +182,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
savedObjects: context.core.savedObjects.client,
cryptography,
customApiSchemaRegistryPromise,
request: req,
authRegistry,
});
},
legacy: {
Expand All @@ -190,6 +193,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
savedObjects: context.core.savedObjects.client,
cryptography,
customApiSchemaRegistryPromise,
request: req,
authRegistry,
});
},
},
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/data_source/server/routes/test_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { DataSourceServiceSetup } from '../data_source_service';
import { CryptographyServiceSetup } from '../cryptography_service';
import { IAuthenticationMethodRegistery } from '../auth_registry';

export const registerTestConnectionRoute = (
export const registerTestConnectionRoute = async (
router: IRouter,
dataSourceServiceSetup: DataSourceServiceSetup,
cryptography: CryptographyServiceSetup,
authRegistryPromise: Promise<IAuthenticationMethodRegistery>
) => {
const authRegistry = await authRegistryPromise;
router.post(
{
path: '/internal/data-source-management/validate',
Expand Down Expand Up @@ -65,6 +66,8 @@ export const registerTestConnectionRoute = (
cryptography,
dataSourceId,
testClientDataSourceAttr: dataSourceAttr as DataSourceAttributes,
request,
authRegistry,
}
);

Expand Down
4 changes: 4 additions & 0 deletions src/plugins/data_source/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export interface DataSourceClientParams {
testClientDataSourceAttr?: DataSourceAttributes;
// custom API schema registry promise, required for getting registered custom API schema
customApiSchemaRegistryPromise: Promise<CustomApiSchemaRegistry>;
// When client parameters are required to be retrieved from the request header, the caller should provide the request.
request?: OpenSearchDashboardsRequest;
// To retrieve the credentials provider for the authentication method from the registry in order to return the client.
authRegistry?: IAuthenticationMethodRegistery;
}

export interface DataSourceCredentialsProviderOptions {
Expand Down
14 changes: 14 additions & 0 deletions src/plugins/data_source/server/util/credential_provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { DataSourceCredentialsProviderOptions, AuthenticationMethod } from '../types';

export const authRegistryCredentialProvider = async (
authenticationMethod: AuthenticationMethod,
options: DataSourceCredentialsProviderOptions
) => ({
credential: await authenticationMethod.credentialProvider(options),
type: authenticationMethod.authType,
});

0 comments on commit 71248cc

Please sign in to comment.