Skip to content

Commit

Permalink
fix(NODE-6284): make sparsity and trimFactor optional (#4189)
Browse files Browse the repository at this point in the history
  • Loading branch information
baileympearson committed Aug 20, 2024
1 parent 1ce2f0d commit 8622545
Show file tree
Hide file tree
Showing 84 changed files with 1,312 additions and 354 deletions.
7 changes: 3 additions & 4 deletions .evergreen/install-mongodb-client-encryption.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#! /usr/bin/env bash
set +o xtrace # Do not write AWS credentials to stderr
set +o xtrace

# Initial checks for running these tests
if [ -z ${PROJECT_DIRECTORY+omitted} ]; then echo "PROJECT_DIRECTORY is unset" && exit 1; fi
Expand All @@ -9,20 +9,19 @@ source "${PROJECT_DIRECTORY}/.evergreen/init-node-and-npm-env.sh"
set -o xtrace # Write all commands first to stderr
set -o errexit # Exit the script with error if any of the commands fail

rm -rf $INSTALL_DIR
rm -rf mongodb-client-encryption
git clone https://github.com/mongodb-js/mongodb-client-encryption.git
pushd mongodb-client-encryption

if [ -n "${LIBMONGOCRYPT_VERSION}" ]; then
# nightly tests test with `latest` to test against the laster FLE build.
npm run install:libmongocrypt -- --libVersion $LIBMONGOCRYPT_VERSION
npm run install:libmongocrypt -- --build --libVersion $LIBMONGOCRYPT_VERSION
else
# otherwise use whatever is specified in the package.json.
npm run install:libmongocrypt
fi

echo "finished installing libmongocrypt"
BINDINGS_DIR=$(pwd)

popd

Expand Down
25 changes: 4 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"js-yaml": "^4.1.0",
"mocha": "^10.4.0",
"mocha-sinon": "^2.1.2",
"mongodb-client-encryption": "^6.1.0-alpha.0",
"mongodb-client-encryption": "^6.1.0",
"mongodb-legacy": "^6.0.1",
"nyc": "^15.1.0",
"prettier": "^2.8.8",
Expand Down
20 changes: 17 additions & 3 deletions src/client-side-encryption/client_encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import type {
MongoCryptOptions
} from 'mongodb-client-encryption';

import { type Binary, deserialize, type Document, type Long, serialize, type UUID } from '../bson';
import {
type Binary,
deserialize,
type Document,
type Int32,
type Long,
serialize,
type UUID
} from '../bson';
import { type AnyBulkWriteOperation, type BulkWriteResult } from '../bulk/common';
import { type ProxyOptions } from '../cmap/connection';
import { type Collection } from '../collection';
Expand Down Expand Up @@ -956,13 +964,19 @@ export interface ClientEncryptionRewrapManyDataKeyResult {
/**
* @public
* RangeOptions specifies index options for a Queryable Encryption field supporting "rangePreview" queries.
* min, max, sparsity, and range must match the values set in the encryptedFields of the destination collection.
* min, max, sparsity, trimFactor and range must match the values set in the encryptedFields of the destination collection.
* For double and decimal128, min/max/precision must all be set, or all be unset.
*/
export interface RangeOptions {
/** min is the minimum value for the encrypted index. Required if precision is set. */
min?: any;
/** max is the minimum value for the encrypted index. Required if precision is set. */
max?: any;
sparsity: Long;
/** sparsity may be used to tune performance. must be non-negative. When omitted, a default value is used. */
sparsity?: Long | bigint;
/** trimFactor may be used to tune performance. must be non-negative. When omitted, a default value is used. */
trimFactor?: Int32 | number;
/* precision determines the number of significant digits after the decimal point. May only be set for double or decimal128. */
precision?: number;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { expect } from 'chai';

/* eslint-disable @typescript-eslint/no-restricted-imports */
import { ClientEncryption } from '../../../src/client-side-encryption/client_encryption';
import { type Binary, EJSON, Int32, Long } from '../../mongodb';
import { installNodeDNSWorkaroundHooks } from '../../tools/runner/hooks/configuration';

const metaData: MongoDBMetadataUI = {
requires: {
clientSideEncryption: '>=6.1.0',

// The Range Explicit Encryption tests require MongoDB server 7.0+ for QE v2.
// The tests must not run against a standalone.
//
// `range` is not supported on 8.0+ servers.
mongodb: '>=8.0.0',
topology: '!single'
}
};

const getKmsProviders = (): { local: { key: string } } => {
const result = EJSON.parse(process.env.CSFLE_KMS_PROVIDERS || '{}') as unknown as {
local: { key: string };
};

return { local: result.local };
};

describe('Range Explicit Encryption Defaults', function () {
installNodeDNSWorkaroundHooks();

let clientEncryption: ClientEncryption;
let keyId;
let keyVaultClient;
let payload_defaults: Binary;

beforeEach(async function () {
// Create a MongoClient named `keyVaultClient`.
keyVaultClient = this.configuration.newClient();

// Create a ClientEncryption object named `clientEncryption` with these options:
// ```typescript
// class ClientEncryptionOpts {
// keyVaultClient: keyVaultClient,
// keyVaultNamespace: "keyvault.datakeys",
// kmsProviders: { "local": { "key": "<base64 decoding of LOCAL_MASTERKEY>" } },
// }
// ```
clientEncryption = new ClientEncryption(keyVaultClient, {
keyVaultNamespace: 'keyvault.datakeys',
kmsProviders: getKmsProviders()
});

// Create a key with `clientEncryption.createDataKey`. Store the returned key ID in a variable named `keyId`.
keyId = await clientEncryption.createDataKey('local');

// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
// ```typescript
// class EncryptOpts {
// keyId : keyId,
// algorithm: "Range",
// contentionFactor: 0,
// rangeOpts: RangeOpts {
// min: 0,
// max: 1000
// }
// }
// ```
// Store the result in a variable named `payload_defaults`.
payload_defaults = await clientEncryption.encrypt(new Int32(123), {
keyId,
algorithm: 'Range',
contentionFactor: 0,
rangeOptions: {
min: 0,
max: 1000
}
});
});

afterEach(async function () {
await keyVaultClient.close();
});

it('Case 1: Uses libmongocrypt defaults', metaData, async function () {
// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
// ```typescript
// class EncryptOpts {
// keyId : keyId,
// algorithm: "Range",
// contentionFactor: 0,
// rangeOpts: RangeOpts {
// min: 0,
// max: 1000,
// sparsity: 2,
// trimFactor: 6
// }
// }
// ```
const encrypted = await clientEncryption.encrypt(new Int32(123), {
keyId: keyId,
algorithm: 'Range',
contentionFactor: 0,
rangeOptions: {
min: 0,
max: 1000,
sparsity: new Long(2),
trimFactor: new Int32(6)
}
});

// Assert the returned payload size equals the size of `payload_defaults`.
expect(encrypted.length()).to.equal(payload_defaults.length());
});

it('Case 2: can find encrypted range and return the maximum', metaData, async function () {
// Call `clientEncryption.encrypt` to encrypt the int32 value `123` with these options:
// ```typescript
// class EncryptOpts {
// keyId : keyId,
// algorithm: "Range",
// contentionFactor: 0,
// rangeOpts: RangeOpts {
// min: 0,
// max: 1000,
// trimFactor: 0
// }
// }
// ```
const encrypted = await clientEncryption.encrypt(new Int32(123), {
keyId: keyId,
algorithm: 'Range',
contentionFactor: 0,
rangeOptions: {
min: 0,
max: 1000,
trimFactor: new Int32(0)
}
});

// Assert the returned payload size is greater than the size of `payload_defaults`.
expect(encrypted.length()).to.be.greaterThan(payload_defaults.length());
});
});
71 changes: 71 additions & 0 deletions test/integration/client-side-encryption/driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,3 +405,74 @@ describe('Client Side Encryption Functional', function () {
}
);
});

describe('Range Explicit Encryption with JS native types', function () {
installNodeDNSWorkaroundHooks();

const metaData: MongoDBMetadataUI = {
requires: {
clientSideEncryption: '>=6.1.0',

// The Range Explicit Encryption tests require MongoDB server 7.0+ for QE v2.
// The tests must not run against a standalone.
//
// `range` is not supported on 8.0+ servers.
mongodb: '>=8.0.0',
topology: '!single'
}
};

const getKmsProviders = (): { local: { key: string } } => {
const result = EJSON.parse(process.env.CSFLE_KMS_PROVIDERS || '{}') as unknown as {
local: { key: string };
};

return { local: result.local };
};

let clientEncryption: ClientEncryption;
let keyId;
let keyVaultClient;

beforeEach(async function () {
keyVaultClient = this.configuration.newClient();
clientEncryption = new ClientEncryption(keyVaultClient, {
keyVaultNamespace: 'keyvault.datakeys',
kmsProviders: getKmsProviders()
});

keyId = await clientEncryption.createDataKey('local');
});

afterEach(async function () {
await keyVaultClient.close();
});

it('supports a js number for trimFactor', metaData, async function () {
await clientEncryption.encrypt(new BSON.Int32(123), {
keyId,
algorithm: 'Range',
contentionFactor: 0,
rangeOptions: {
min: 0,
max: 1000,
trimFactor: 1,
sparsity: new BSON.Long(1)
}
});
});

it('supports a bigint for sparsity', metaData, async function () {
await clientEncryption.encrypt(new BSON.Int32(123), {
keyId,
algorithm: 'Range',
contentionFactor: 0,
rangeOptions: {
min: 0,
max: 1000,
trimFactor: new BSON.Int32(1),
sparsity: 1n
}
});
});
});
Loading

0 comments on commit 8622545

Please sign in to comment.