Skip to content

Commit

Permalink
[MD]Use placeholder for data source credentials fields when export sa…
Browse files Browse the repository at this point in the history
…ved object (opensearch-project#6928)

* [MD]Use placeholder for data source credentials fields  when exporting saved object

Signed-off-by: Zhongnan Su <[email protected]>

* Changeset file for PR opensearch-project#6928 created/updated

---------

Signed-off-by: Zhongnan Su <[email protected]>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
1 parent f51f652 commit 0188efe
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 2 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/6928.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- [MD]Use placeholder for data source credentials fields when export saved object ([#6928](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6928))
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
* under the License.
*/

import { exportSavedObjectsToStream } from './get_sorted_objects_for_export';
import {
DATA_SOURCE_CREDENTIALS_PLACEHOLDER,
exportSavedObjectsToStream,
} from './get_sorted_objects_for_export';
import { savedObjectsClientMock } from '../service/saved_objects_client.mock';
import { Readable } from 'stream';
import { createPromiseFromStreams, createConcatStream } from '../../utils/streams';
Expand Down Expand Up @@ -706,6 +709,50 @@ describe('getSortedObjectsForExport()', () => {
]);
});

test('modifies return results to update `credentials` of data-source to use placeholder', async () => {
const createDataSourceSavedObject = (id: string, auth: any) => ({
id,
type: 'data-source',
attributes: { auth },
references: [],
});

const dataSourceNoAuthInfo = { type: 'no_auth' };
const dataSourceBasicAuthInfo = {
type: 'username_password',
credentials: { username: 'foo', password: 'bar' },
};

const redactedDataSourceBasicAuthInfo = {
type: 'username_password',
credentials: {
username: DATA_SOURCE_CREDENTIALS_PLACEHOLDER,
password: DATA_SOURCE_CREDENTIALS_PLACEHOLDER,
},
};

savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
createDataSourceSavedObject('1', dataSourceNoAuthInfo),
createDataSourceSavedObject('2', dataSourceBasicAuthInfo),
],
});
const exportStream = await exportSavedObjectsToStream({
exportSizeLimit: 10000,
savedObjectsClient,
objects: [
{ type: 'data-source', id: '1' },
{ type: 'data-source', id: '2' },
],
});
const response = await readStreamToCompletion(exportStream);
expect(response).toEqual([
createDataSourceSavedObject('1', dataSourceNoAuthInfo),
createDataSourceSavedObject('2', redactedDataSourceBasicAuthInfo),
expect.objectContaining({ exportedCount: 2 }),
]);
});

test('includes nested dependencies when passed in', async () => {
savedObjectsClient.bulkGet.mockResolvedValueOnce({
saved_objects: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { SavedObjectsClientContract, SavedObject, SavedObjectsBaseOptions } from
import { fetchNestedDependencies } from './inject_nested_depdendencies';
import { sortObjects } from './sort_objects';

export const DATA_SOURCE_CREDENTIALS_PLACEHOLDER = 'pleaseUpdateCredentials';

/**
* Options controlling the export operation.
* @public
Expand Down Expand Up @@ -185,10 +187,40 @@ export async function exportSavedObjectsToStream({
({ namespaces, ...object }) => object
);

// update the credential fields from "data-source" saved object to use placeholder to avoid exporting sensitive information
const redactedObjectsWithoutCredentials = redactedObjects.map<SavedObject<unknown>>((object) => {
if (object.type === 'data-source') {
const { auth, ...rest } = object.attributes as {
auth: { type: string; credentials?: any };
};
const hasCredentials = auth && auth.credentials;
const updatedCredentials = hasCredentials
? Object.keys(auth.credentials).reduce((acc, key) => {
acc[key] = DATA_SOURCE_CREDENTIALS_PLACEHOLDER;
return acc;
}, {} as { [key: string]: any })
: undefined;
return {
...object,
attributes: {
...rest,
auth: {
type: auth.type,
...(hasCredentials && { credentials: updatedCredentials }),
},
},
};
}
return object;
});

const exportDetails: SavedObjectsExportResultDetails = {
exportedCount: exportedObjects.length,
missingRefCount: missingReferences.length,
missingReferences,
};
return createListStream([...redactedObjects, ...(excludeExportDetails ? [] : [exportDetails])]);
return createListStream([
...redactedObjectsWithoutCredentials,
...(excludeExportDetails ? [] : [exportDetails]),
]);
}

0 comments on commit 0188efe

Please sign in to comment.