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 14, 2024
1 parent e08bf30 commit 174cf73
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 21 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
55 changes: 44 additions & 11 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,9 +27,18 @@ import {
getDataSource,
generateCacheKey,
} from './configure_client_utils';
import { IAuthenticationMethodRegistery } from '../auth_registry';
import { authRegistryCredentialProvider } from '../util/credential_provider';

export const configureClient = async (
{ dataSourceId, savedObjects, cryptography, testClientDataSourceAttr }: DataSourceClientParams,
{
dataSourceId,
savedObjects,
cryptography,
testClientDataSourceAttr,
request,
authRegistry,
}: DataSourceClientParams,
openSearchClientPoolSetup: OpenSearchClientPoolSetup,
config: DataSourcePluginConfigType,
logger: Logger
Expand Down Expand Up @@ -71,6 +80,8 @@ export const configureClient = async (
cryptography,
rootClient,
dataSourceId,
request,
authRegistry,
requireDecryption
);
} catch (error: any) {
Expand All @@ -91,6 +102,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 @@ -101,15 +114,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);
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 @@ -118,21 +147,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
37 changes: 29 additions & 8 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,9 +35,11 @@ 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 }: DataSourceClientParams,
{ dataSourceId, savedObjects, cryptography, request, authRegistry }: DataSourceClientParams,
callApiParams: LegacyClientCallAPIParams,
openSearchClientPoolSetup: OpenSearchClientPoolSetup,
config: DataSourcePluginConfigType,
Expand All @@ -57,7 +60,9 @@ export const configureLegacyClient = async (
openSearchClientPoolSetup.addClientToPool,
config,
rootClient,
dataSourceId
dataSourceId,
request,
authRegistry
);
} catch (error: any) {
logger.debug(
Expand Down Expand Up @@ -86,15 +91,31 @@ const getQueryClient = async (
addClientToPool: (endpoint: string, authType: AuthType, client: Client | LegacyClient) => void,
config: DataSourcePluginConfigType,
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);
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 @@ -107,17 +128,17 @@ const getQueryClient = async (
);

case AuthType.UsernamePasswordType:
const credential = await getCredential(dataSourceAttr, cryptography);
credential = 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 = 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 @@ -159,7 +159,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
auditTrailPromise: Promise<AuditorFactory>,
authRegistryPromise: Promise<IAuthenticationMethodRegistery>
): IContextProvider<RequestHandler<unknown, unknown, unknown>, 'dataSource'> => {
return (context, req) => {
return async (context, req) => {
const authRegistry = await authRegistryPromise;
return {
opensearch: {
getClient: (dataSourceId: string) => {
Expand All @@ -171,6 +172,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
dataSourceId,
savedObjects: context.core.savedObjects.client,
cryptography,
request: req,
authRegistry,
});
},
legacy: {
Expand All @@ -179,6 +182,8 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
dataSourceId,
savedObjects: context.core.savedObjects.client,
cryptography,
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 @@ -34,6 +34,10 @@ export interface DataSourceClientParams {
dataSourceId?: string;
// required when creating test client
testClientDataSourceAttr?: DataSourceAttributes;
// 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 174cf73

Please sign in to comment.