Skip to content

Commit

Permalink
feat(connection-form): support for multiple kms options from same pro…
Browse files Browse the repository at this point in the history
…vider COMPASS-8082 (#6166)

* ux changes

* fix tests

* fix after merge

* tests

* ts issues

* remove todo

* store connection data

* use icon button

* fix types

* fix test

* another ts

* fix connection storage test

* show delete button on hover

* show configured providers when creating collection

* multiple local keys in tests

* multiple local keys in tests

* allow rename of kms

* tests

* don't clear state when modal is closed

* check

* pr feedback

* cleaner path names

* e2e-test

* remove tls

* fix id

* use regex to get next name

* reformat doesn't take care of ts-comments

* cr fix

* revert unnecessary changes
  • Loading branch information
mabaasit authored Sep 9, 2024
1 parent baecaef commit aa7066c
Show file tree
Hide file tree
Showing 25 changed files with 1,718 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1549,12 +1549,15 @@ export const connect = (
dispatch(disconnect(connectionInfo.id));
});

const { connectionOptions, ...restOfTheConnectionInfo } =
connectionInfo;

const adjustedConnectionInfoForConnection: ConnectionInfo = merge(
cloneDeep(connectionInfo),
cloneDeep(restOfTheConnectionInfo),
{
connectionOptions: adjustConnectionOptionsBeforeConnect({
connectionOptions: merge(
cloneDeep(connectionInfo.connectionOptions),
cloneDeep(connectionOptions),
SecretsForConnection.get(connectionInfo.id) ?? {}
),
defaultAppName: appName,
Expand Down
32 changes: 26 additions & 6 deletions packages/compass-e2e-tests/helpers/commands/connect-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,18 @@ function colorValueToName(color: string): string {
return color;
}

async function setKMSProviderName(
browser: CompassBrowser,
index: number,
name: string
) {
await browser.clickVisible(Selectors.connectionFormEditFLEName(index));
return await browser.setValueVisible(
Selectors.connectionFormInputFLELocalName(index),
name
);
}

export async function setConnectFormState(
browser: CompassBrowser,
state: ConnectFormState
Expand Down Expand Up @@ -654,7 +666,7 @@ export async function setConnectFormState(
// FLE2
if (
state.fleKeyVaultNamespace ||
state.fleKey ||
state.kmsProviders ||
state.fleEncryptedFieldsMap
) {
await browser.navigateToConnectTab('In-Use Encryption');
Expand All @@ -665,12 +677,20 @@ export async function setConnectFormState(
state.fleKeyVaultNamespace
);
}
if (state.fleKey) {
if ((state.kmsProviders?.local?.length ?? 0) > 0) {
await browser.expandAccordion(Selectors.ConnectionFormInputFLELocalKMS);
await browser.setValueVisible(
Selectors.ConnectionFormInputFLELocalKey,
state.fleKey
);
for (const [index, item] of (state.kmsProviders?.local ?? []).entries()) {
if (item.name) {
await setKMSProviderName(browser, index, item.name);
}
await browser.setValueVisible(
Selectors.connectionFormInputFLELocalKey(index),
item.key
);
await browser.clickVisible(
Selectors.ConnectionFormAddNewKMSProviderButton
);
}
}
if (state.fleEncryptedFieldsMap) {
// set the text in the editor
Expand Down
8 changes: 7 additions & 1 deletion packages/compass-e2e-tests/helpers/connect-form-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,14 @@ export interface ConnectFormState {
// FLE2
fleKeyVaultNamespace?: string;
fleStoreCredentials?: boolean;
fleKey?: string;
fleEncryptedFieldsMap?: string;
kmsProviders?: {
// For now adding support for local only
local?: {
name?: string;
key: string;
}[];
};

// - SSH with Password
sshPasswordHost?: string;
Expand Down
10 changes: 8 additions & 2 deletions packages/compass-e2e-tests/helpers/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,14 @@ export const ConnectionFormInputFLEStoreCredentialsCheckbox =
'[data-testid="csfle-store-credentials-input"]';
export const ConnectionFormInputFLELocalKMS =
'[data-testid="csfle-kms-provider-local"]';
export const ConnectionFormInputFLELocalKey =
'[data-testid="csfle-kms-local-key"]';
export const connectionFormEditFLEName = (index = 0) =>
`[data-card-index="${index}"] [data-testid="csfle-edit-kms-name"]`;
export const connectionFormInputFLELocalName = (index = 0) =>
`[data-card-index="${index}"] [data-testid="csfle-kms-card-name"]`;
export const connectionFormInputFLELocalKey = (index = 0) =>
`[data-card-index="${index}"] [data-testid="csfle-kms-local-key"]`;
export const ConnectionFormAddNewKMSProviderButton =
'[data-testid="csfle-add-new-kms-provider-button"]';
export const ConnectionFormInputFLEEncryptedFieldsMap =
'[data-testid="connection-csfle-encrypted-fields-map"]';
export const ConnectionFormInputFLEEncryptedFieldsMapEditor =
Expand Down
8 changes: 7 additions & 1 deletion packages/compass-e2e-tests/tests/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,13 @@ describe('FLE2', function () {
await browser.connectWithConnectionForm({
hosts: ['127.0.0.1:27091'],
fleKeyVaultNamespace: 'alena.keyvault',
fleKey: 'A'.repeat(128),
kmsProviders: {
local: [
{
key: 'A'.repeat(128),
},
],
},
fleEncryptedFieldsMap: `{
'alena.coll': {
fields: [
Expand Down
187 changes: 182 additions & 5 deletions packages/compass-e2e-tests/tests/in-use-encryption.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ describe('CSFLE / QE', function () {
const options: ConnectFormState = {
hosts: [CONNECTION_HOSTS],
fleKeyVaultNamespace: `${databaseName}.keyvault`,
fleKey: 'A'.repeat(128),
kmsProviders: {
local: [
{
key: 'A'.repeat(128),
},
],
},
fleEncryptedFieldsMap: `{
'${databaseName}.${collectionName}': {
fields: [
Expand Down Expand Up @@ -226,7 +232,14 @@ describe('CSFLE / QE', function () {
await browser.connectWithConnectionForm({
hosts: [CONNECTION_HOSTS],
fleKeyVaultNamespace: `${databaseName}.keyvault`,
fleKey: 'A'.repeat(128),
kmsProviders: {
local: [
{
name: 'local',
key: 'A'.repeat(128),
},
],
},
connectionName,
});

Expand Down Expand Up @@ -324,7 +337,14 @@ describe('CSFLE / QE', function () {
await browser.connectWithConnectionForm({
hosts: [CONNECTION_HOSTS],
fleKeyVaultNamespace: `${databaseName}.keyvault`,
fleKey: 'A'.repeat(128),
kmsProviders: {
local: [
{
name: 'local',
key: 'A'.repeat(128),
},
],
},
fleEncryptedFieldsMap: `{
'${databaseName}.${collectionName}': {
fields: [
Expand Down Expand Up @@ -372,7 +392,7 @@ describe('CSFLE / QE', function () {
'"creationDate": ISODate("2022-05-27T18:28:33.925Z"),' +
'"updateDate": ISODate("2022-05-27T18:28:33.925Z"),' +
'"status": 0,' +
'"masterKey": { "provider" : "local" }' +
'"masterKey": { "provider" : "local:local" }' +
'})',
// make sure there is a collection so we can navigate to the database
`db.getMongo().getDB('${databaseName}').createCollection('default')`,
Expand Down Expand Up @@ -947,6 +967,157 @@ describe('CSFLE / QE', function () {
});
});
});

describe('multiple kms providers of the same type', function () {
const databaseName = 'fle-test';
const collection1 = 'collection-1';
const collection2 = 'collection-2';
const phoneNumber1 = '1234567890';
const phoneNumber2 = '0987654321';
let compass: Compass;
let browser: CompassBrowser;
let plainMongo: MongoClient;

before(async function () {
compass = await init(this.test?.fullTitle());
browser = compass.browser;
});

beforeEach(async function () {
await browser.disconnectAll();
await browser.connectWithConnectionForm({
hosts: [CONNECTION_HOSTS],
fleKeyVaultNamespace: `${databaseName}.keyvault`,
fleEncryptedFieldsMap: `{
'${databaseName}.${collection1}': {
fields: [
{
path: 'phoneNumber',
keyId: UUID("28bbc608-524e-4717-9246-33633361788e"),
bsonType: 'string',
queries: { queryType: 'equality' }
}
]
},
'${databaseName}.${collection2}': {
fields: [
{
path: 'phoneNumber',
keyId: UUID("9c932ef9-f43c-489a-98f3-31012a83bc46"),
bsonType: 'string',
queries: { queryType: 'equality' }
}
]
},
}`,
kmsProviders: {
local: [
{
name: 'localA',
key: 'A'.repeat(128),
},
{
name: 'localB',
key: 'B'.repeat(128),
},
],
},
connectionName,
});
await browser.shellEval(connectionName, [
`use ${databaseName}`,
'db.keyvault.insertOne({' +
'"_id": UUID("28bbc608-524e-4717-9246-33633361788e"),' +
'"keyMaterial": Binary.createFromBase64("fqZuVyi6ThsSNbgUWtn9MCFDxOQtL3dibMa2P456l+1xJUvAkqzZB2SZBr5Zd2xLDua45IgYAagWFeLhX+hpi0KkdVgdIZu2zlZ+mJSbtwZrFxcuyQ3oPCPnp7l0YH1fSfxeoEIQNVMFpnHzfbu2CgZ/nC8jp6IaB9t+tcszTDdJRLeHnzPuHIKzblFGP8CfuQHJ81B5OA0PrBJr+HbjJg==", 0),' +
'"creationDate": ISODate("2022-05-27T18:28:33.925Z"),' +
'"updateDate": ISODate("2022-05-27T18:28:33.925Z"),' +
'"status": 0,' +
'"masterKey": { "provider" : "local:localA" }' +
'})',
'db.keyvault.insertOne({' +
'"_id": UUID("9c932ef9-f43c-489a-98f3-31012a83bc46"),' +
'"keyMaterial": Binary.createFromBase64("TymoH++xeTsaiIl498fviLaklY4xTM/baQydmVUABphJzvBsitjWfkoiKlGod/J45Vwoou1VfDRsFaiVHNth7aiFBvEsqvto5ETDFC9hSzP17c1ZrQI1nqrOfI0VGJm+WBALB7IMVFuyd9LV2i6KDIslxBfchOGR4q05Gm1Vgb/cTTUPJpvYLxmduyNSjxqH6lBAJ2ut9TgmUxCC+dMQRQ==", 0),' +
'"creationDate": ISODate("2022-05-27T18:28:34.925Z"),' +
'"updateDate": ISODate("2022-05-27T18:28:34.925Z"),' +
'"status": 0,' +
'"masterKey": { "provider" : "local:localB" }' +
'})',
// make sure there is a collection so we can navigate to the database
`db.getMongo().getDB('${databaseName}').createCollection('default')`,
]);
await refresh(browser, connectionName);

plainMongo = await MongoClient.connect(CONNECTION_STRING);
});

after(async function () {
if (compass) {
await cleanup(compass);
}
});

afterEach(async function () {
if (compass) {
await screenshotIfFailed(compass, this.currentTest);
}
await plainMongo.db(databaseName).dropDatabase();
await plainMongo.close();
});

it('allows setting multiple kms providers of the same type', async function () {
async function verifyCollectionHasValue(
collection: string,
value: string
) {
await browser.navigateToCollectionTab(
connectionName,
databaseName,
collection,
'Documents'
);
const result = await getFirstListDocument(browser);
expect(result.phoneNumber).to.be.equal(JSON.stringify(value));
}

await browser.shellEval(connectionName, [
`use ${databaseName}`,
`db.createCollection("${collection1}")`,
`db.createCollection("${collection2}")`,
`db["${collection1}"].insertOne({ "phoneNumber": "${phoneNumber1}", "name": "LocalA" })`,
`db["${collection2}"].insertOne({ "phoneNumber": "${phoneNumber2}", "name": "LocalB" })`,
]);
await refresh(browser, connectionName);

await verifyCollectionHasValue(collection1, phoneNumber1);
await verifyCollectionHasValue(collection2, phoneNumber2);

// create a new encrypted collection using keyId for local:localB
await browser.navigateToDatabaseCollectionsTab(
connectionName,
databaseName
);
const collection3 = 'collection-3';
const phoneNumber3 = '1111111111';
await browser.clickVisible(Selectors.DatabaseCreateCollectionButton);
await browser.addCollection(collection3, {
encryptedFields: `{
fields: [{
path: 'phoneNumber',
keyId: UUID("9c932ef9-f43c-489a-98f3-31012a83bc46"),
bsonType: 'string',
queries: { queryType: 'equality' }
}]
}`,
});

await browser.shellEval(connectionName, [
`use ${databaseName}`,
`db["${collection3}"].insertOne({ "phoneNumber": "${phoneNumber3}", "name": "LocalB" })`,
]);

await verifyCollectionHasValue(collection3, phoneNumber3);
});
});
});

describe('server version gte 6.0 and lt 7.0', function () {
Expand Down Expand Up @@ -1070,7 +1241,13 @@ describe('CSFLE / QE', function () {
await browser.connectWithConnectionForm({
hosts: [CONNECTION_HOSTS],
fleKeyVaultNamespace: `${databaseName}.keyvault`,
fleKey: 'A'.repeat(128),
kmsProviders: {
local: [
{
key: 'A'.repeat(128),
},
],
},
connectionName,
});

Expand Down
Loading

0 comments on commit aa7066c

Please sign in to comment.