Skip to content

Commit

Permalink
Revise data source saved object model
Browse files Browse the repository at this point in the history
Signed-off-by: Louis Chu <clingzhi@amazon.com>
  • Loading branch information
noCharger committed Sep 1, 2022
1 parent 5b62400 commit 228b2b2
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 83 deletions.
10 changes: 5 additions & 5 deletions src/plugins/data_source/common/data_sources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ export interface DataSourceAttributes extends SavedObjectAttributes {
title: string;
description?: string;
endpoint: string;
noAuth: boolean;
credentials?: {
type: CredentialsType;
credentialsContent: UsernamePasswordTypedContent;
auth: {
type: AuthType;
credentials: UsernamePasswordTypedContent | undefined;
};
}

Expand All @@ -21,6 +20,7 @@ export interface UsernamePasswordTypedContent extends SavedObjectAttributes {
password: string;
}

export enum CredentialsType {
export enum AuthType {
NoAuth = 'no_auth',
UsernamePasswordType = 'username_password',
}
18 changes: 11 additions & 7 deletions src/plugins/data_source/server/client/configure_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { SavedObjectsClientContract } from '../../../../core/server';
import { loggingSystemMock, savedObjectsClientMock } from '../../../../core/server/mocks';
import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../common';
import { DataSourceAttributes, CredentialsType } from '../../common/data_sources/types';
import { DataSourceAttributes, AuthType } from '../../common/data_sources/types';
import { DataSourcePluginConfigType } from '../../config';
import { ClientMock, parseClientOptionsMock } from './configure_client.test.mocks';
import { OpenSearchClientPoolSetup } from './client_pool';
Expand Down Expand Up @@ -49,10 +49,9 @@ describe('configureClient', () => {
dataSourceAttr = {
title: 'title',
endpoint: 'http://localhost',
noAuth: false,
credentials: {
type: CredentialsType.UsernamePasswordType,
credentialsContent: {
auth: {
type: AuthType.UsernamePasswordType,
credentials: {
username: 'username',
password: 'password',
},
Expand Down Expand Up @@ -80,11 +79,16 @@ describe('configureClient', () => {
ClientMock.mockReset();
});

test('configure client with noAuth == true, will call new Client() to create client', async () => {
test('configure client with auth.type == no_auth, will call new Client() to create client', async () => {
savedObjectsMock.get.mockReset().mockResolvedValueOnce({
id: DATA_SOURCE_ID,
type: DATA_SOURCE_SAVED_OBJECT_TYPE,
attributes: { ...dataSourceAttr, noAuth: true },
attributes: {
...dataSourceAttr,
auth: {
type: AuthType.NoAuth,
},
},
references: [],
});

Expand Down
12 changes: 8 additions & 4 deletions src/plugins/data_source/server/client/configure_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
} from '../../../../../src/core/server';
import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../common';

import { DataSourceAttributes, UsernamePasswordTypedContent } from '../../common/data_sources';
import {
AuthType,
DataSourceAttributes,
UsernamePasswordTypedContent,
} from '../../common/data_sources';
import { DataSourcePluginConfigType } from '../../config';
import { CryptographyClient } from '../cryptography';
import { parseClientOptions } from './client_config';
Expand Down Expand Up @@ -53,7 +57,7 @@ export const getCredential = async (
cryptographyClient: CryptographyClient
): Promise<UsernamePasswordTypedContent> => {
try {
const { username, password } = dataSource.attributes.credentials!.credentialsContent;
const { username, password } = dataSource.attributes.auth.credentials!;
const decodedPassword = await cryptographyClient.decodeAndDecrypt(password);
const credential = {
username,
Expand All @@ -72,15 +76,15 @@ export const getCredential = async (
*
* @param rootClient root client for the connection with given data source endpoint.
* @param dataSource data source saved object
* @param savedObjects scoped saved object client
* @param cryptographyClient cryptography client for password encryption / decrpytion
* @returns child client.
*/
const getQueryClient = async (
rootClient: Client,
dataSource: SavedObject<DataSourceAttributes>,
cryptographyClient: CryptographyClient
): Promise<Client> => {
if (dataSource.attributes.noAuth) {
if (AuthType.NoAuth === dataSource.attributes.auth.type) {
return rootClient.child();
} else {
const credential = await getCredential(dataSource, cryptographyClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { SavedObjectsErrorHelpers } from '../../../../core/server';
import { CryptographyClient } from '../cryptography';

import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../../common';
import { CredentialsType } from '../../common/data_sources';
import { AuthType } from '../../common/data_sources';

/**
* Describes the Credential Saved Objects Client Wrapper class,
Expand Down Expand Up @@ -137,56 +137,52 @@ export class DataSourceSavedObjectsClientWrapper {
}
}

private dropCredentials<T = unknown>(attributes: Omit<T, 'credentials'>) {
return attributes;
}

private async validateAndEncryptAttributes<T = unknown>(attributes: T) {
this.validateAttributes(attributes);

const { noAuth } = attributes;

// Drop credentials when no Auth
if (!noAuth) {
return this.dropCredentials(attributes);
}

const { type, credentialsContent } = attributes.credentials;
const { type, credentials } = attributes.auth;

switch (type) {
case CredentialsType.UsernamePasswordType:
const { username, password } = credentialsContent;
case AuthType.NoAuth:
return {
...attributes,
// Drop the credentials attribute for no_auth
credentials: undefined,
};
case AuthType.UsernamePasswordType:
const { username, password } = credentials;
return {
...attributes,
credentials: {
type,
credentialsContent: {
username,
password: await this.cryptographyClient.encryptAndEncode(password),
},
username,
password: await this.cryptographyClient.encryptAndEncode(password),
},
};
default:
throw SavedObjectsErrorHelpers.createBadRequestError(
`Invalid credential materials type: '${type}'`
);
throw SavedObjectsErrorHelpers.createBadRequestError(`Invalid auth type: '${type}'`);
}
}

private async validateAndUpdatePartialAttributes<T = unknown>(attributes: T) {
const { noAuth, credentials } = attributes;
const { auth } = attributes;

// Drop credentials when no Auth
if (!noAuth) {
return this.dropCredentials(attributes);
if (auth === undefined) {
return attributes;
}

const { type, credentialsContent } = credentials;
const { type, credentials } = auth;

switch (type) {
case CredentialsType.UsernamePasswordType:
if ('password' in credentialsContent) {
const { username, password } = credentialsContent;
case AuthType.NoAuth:
return {
...attributes,
// Drop the credentials attribute for no_auth
credentials: undefined,
};
case AuthType.UsernamePasswordType:
const { username, password } = credentials;

if (password) {
return {
...attributes,
credentials: {
Expand All @@ -206,61 +202,64 @@ export class DataSourceSavedObjectsClientWrapper {
}

private validateAttributes<T = unknown>(attributes: T) {
const { title, endpoint, noAuth, credentials } = attributes;
const { title, endpoint, auth } = attributes;
if (!title) {
throw SavedObjectsErrorHelpers.createBadRequestError('attribute "title" required');
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "title" required for "data source" saved object'
);
}

if (!this.isValidUrl(endpoint)) {
throw SavedObjectsErrorHelpers.createBadRequestError('attribute "endpoint" is not valid');
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "endpoint" is not valid for "data source" saved object'
);
}

if (noAuth) {
this.validateCredentials(credentials);
if (auth === undefined) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "auth" required for "data source" saved object'
);
}
}

private validateCredentials<T = unknown>(credentials: T) {
if (credentials === undefined) {
throw SavedObjectsErrorHelpers.createBadRequestError('attribute "credentials" required');
}
this.validateAuth(auth);
}

const { type, credentialsContent } = credentials;
private validateAuth<T = unknown>(auth: T) {
const { type, credentials } = auth;

if (!type) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "type" required for "credentials"'
);
}

if (credentialsContent === undefined) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "credentialsContent" required for "credentials"'
'attribute "auth.type" required for "data source" saved object'
);
}

switch (type) {
case CredentialsType.UsernamePasswordType:
const { username, password } = credentialsContent;
case AuthType.NoAuth:
break;
case AuthType.UsernamePasswordType:
if (credentials === undefined) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "auth.credentials" required for "data source" saved object'
);
}

this.validateUsername(username);
this.validatePassword(password);
default:
throw SavedObjectsErrorHelpers.createBadRequestError(`Invalid credentials type: '${type}'`);
}
}
const { username, password } = credentials;

private validateUsername<T = unknown>(username: T) {
if (!username) {
throw SavedObjectsErrorHelpers.createBadRequestError('attribute "username" required');
}
return;
}
if (!username) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "auth.credentials.username" required'
);
}

private validatePassword<T = unknown>(password: T) {
if (!password) {
throw SavedObjectsErrorHelpers.createBadRequestError('attribute "password" required');
if (!password) {
throw SavedObjectsErrorHelpers.createBadRequestError(
'attribute "auth.credentials.password" required'
);
}

break;
default:
throw SavedObjectsErrorHelpers.createBadRequestError(`Invalid auth type: '${type}'`);
}
return;
}
}

0 comments on commit 228b2b2

Please sign in to comment.