Skip to content

Commit

Permalink
finish rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
baileympearson committed Jan 23, 2025
1 parent a6a51bd commit a4b4299
Show file tree
Hide file tree
Showing 14 changed files with 1,594 additions and 332 deletions.
10 changes: 10 additions & 0 deletions docs/field-level-encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,13 @@ To declare a field as encrypted, you must:
2. Choose an encryption type for the schema and configure the schema for the encryption type

Not all schematypes are supported for CSFLE and QE. For an overview of valid schema types, refer to MongoDB's documentation.

### Registering Models

Encrypted schemas must be registered on a connection, not the Mongoose global:

```javascript

const connection = mongoose.createConnection();
const UserModel = connection.model('User', encryptedUserSchema);
```
2 changes: 1 addition & 1 deletion lib/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Collection.prototype.onOpen = function() {
* @api private
*/

Collection.prototype.onClose = function() {};
Collection.prototype.onClose = function() { };

/**
* Queues a method for later execution when its
Expand Down
20 changes: 10 additions & 10 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {

Connection.prototype.createCollections = async function createCollections(options = {}) {
const result = {};
const errorsMap = { };
const errorsMap = {};

const { continueOnError } = options;
delete options.continueOnError;
Expand Down Expand Up @@ -734,7 +734,7 @@ Connection.prototype.transaction = function transaction(fn, options) {
throw err;
}).
finally(() => {
session.endSession().catch(() => {});
session.endSession().catch(() => { });
});
});
};
Expand Down Expand Up @@ -1025,7 +1025,7 @@ Connection.prototype.openUri = async function openUri(uri, options) {

for (const model of Object.values(this.models)) {
// Errors handled internally, so safe to ignore error
model.init().catch(function $modelInitNoop() {});
model.init().catch(function $modelInitNoop() { });
}

// `createConnection()` calls this `openUri()` function without
Expand Down Expand Up @@ -1061,7 +1061,7 @@ Connection.prototype.openUri = async function openUri(uri, options) {
// to avoid uncaught exceptions when using `on('error')`. See gh-14377.
Connection.prototype.on = function on(event, callback) {
if (event === 'error' && this.$initialConnection) {
this.$initialConnection.catch(() => {});
this.$initialConnection.catch(() => { });
}
return EventEmitter.prototype.on.call(this, event, callback);
};
Expand All @@ -1083,7 +1083,7 @@ Connection.prototype.on = function on(event, callback) {
// to avoid uncaught exceptions when using `on('error')`. See gh-14377.
Connection.prototype.once = function on(event, callback) {
if (event === 'error' && this.$initialConnection) {
this.$initialConnection.catch(() => {});
this.$initialConnection.catch(() => { });
}
return EventEmitter.prototype.once.call(this, event, callback);
};
Expand Down Expand Up @@ -1412,7 +1412,7 @@ Connection.prototype.model = function model(name, schema, collection, options) {
}

// Errors handled internally, so safe to ignore error
model.init().catch(function $modelInitNoop() {});
model.init().catch(function $modelInitNoop() { });

return model;
}
Expand All @@ -1439,7 +1439,7 @@ Connection.prototype.model = function model(name, schema, collection, options) {
}

if (this === model.prototype.db
&& (!collection || collection === model.collection.name)) {
&& (!collection || collection === model.collection.name)) {
// model already uses this connection.

// only the first model with this name is cached to allow
Expand Down Expand Up @@ -1626,8 +1626,8 @@ Connection.prototype.authMechanismDoesNotRequirePassword = function authMechanis
*/
Connection.prototype.optionsProvideAuthenticationData = function optionsProvideAuthenticationData(options) {
return (options) &&
(options.user) &&
((options.pass) || this.authMechanismDoesNotRequirePassword());
(options.user) &&
((options.pass) || this.authMechanismDoesNotRequirePassword());
};

/**
Expand Down Expand Up @@ -1689,7 +1689,7 @@ Connection.prototype.createClient = function createClient() {
*/
Connection.prototype.syncIndexes = async function syncIndexes(options = {}) {
const result = {};
const errorsMap = { };
const errorsMap = {};

const { continueOnError } = options;
delete options.continueOnError;
Expand Down
52 changes: 48 additions & 4 deletions lib/drivers/node-mongodb-native/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,16 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
};
}

const { schemaMap, encryptedFieldsMap } = this._buildEncryptionSchemas();

if (Object.keys(schemaMap).length > 0) {
options.autoEncryption.schemaMap = schemaMap;
}

if (Object.keys(encryptedFieldsMap).length > 0) {
options.autoEncryption.encryptedFieldsMap = encryptedFieldsMap;
}

this.readyState = STATES.connecting;
this._connectionString = uri;

Expand All @@ -338,6 +348,40 @@ NativeConnection.prototype.createClient = async function createClient(uri, optio
return this;
};

/**
* Given a connection, which may or may not have encrypted models, build
* a schemaMap and/or an encryptedFieldsMap for the connection, combining all models
* into a single schemaMap and encryptedFields map.
*
* @returns a copy of the options object with a schemaMap and/or an encryptedFieldsMap added to the options' autoEncryption
* options.
*/
NativeConnection.prototype._buildEncryptionSchemas = function() {
const schemaMap = Object.values(this.models).filter(model => model.schema.encryptionType() === 'csfle').reduce(
(schemaMap, model) => {
const { schema, collection: { collectionName } } = model;
const namespace = `${this.$dbName}.${collectionName}`;
schemaMap[namespace] = schema._buildSchemaMap();
return schemaMap;
},
{}
);

const encryptedFieldsMap = Object.values(this.models).filter(model => model.schema.encryptionType() === 'qe').reduce(
(encryptedFieldsMap, model) => {
const { schema, collection: { collectionName } } = model;
const namespace = `${this.$dbName}.${collectionName}`;
encryptedFieldsMap[namespace] = schema._buildEncryptedFields();
return encryptedFieldsMap;
},
{}
);

return {
schemaMap, encryptedFieldsMap
};
};

/*!
* ignore
*/
Expand All @@ -358,7 +402,7 @@ NativeConnection.prototype.setClient = function setClient(client) {

for (const model of Object.values(this.models)) {
// Errors handled internally, so safe to ignore error
model.init().catch(function $modelInitNoop() {});
model.init().catch(function $modelInitNoop() { });
}

return this;
Expand Down Expand Up @@ -401,9 +445,9 @@ function _setClient(conn, client, options, dbName) {
};

const type = client &&
client.topology &&
client.topology.description &&
client.topology.description.type || '';
client.topology &&
client.topology.description &&
client.topology.description.type || '';

if (type === 'Single') {
client.on('serverDescriptionChanged', ev => {
Expand Down
72 changes: 72 additions & 0 deletions lib/encryption_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use strict';

const { Array } = require('./schema/index.js');
const SchemaBigInt = require('./schema/bigint');
const SchemaBoolean = require('./schema/boolean');
const SchemaBuffer = require('./schema/buffer');
const SchemaDate = require('./schema/date');
const SchemaDecimal128 = require('./schema/decimal128');
const SchemaDouble = require('./schema/double');
const SchemaInt32 = require('./schema/int32');
const SchemaObjectId = require('./schema/objectId');
const SchemaString = require('./schema/string');

/**
* Given a schema and a path to a field in the schema, this returns the
* BSON type of the field, if it can be determined. This method specifically
* **only** handles BSON types that are used for CSFLE and QE - any other
* BSON types will return `null`. (example: MinKey and MaxKey).
*
* @param {import('.').Schema} schema
* @param {string} path
* @returns
*/
function inferBSONType(schema, path) {
const type = schema.path(path);

if (type instanceof SchemaString) {
return 'string';
}

if (type instanceof SchemaInt32) {
return 'int';
}

if (type instanceof SchemaBigInt) {
return 'long';
}

if (type instanceof SchemaBoolean) {
return 'bool';
}

if (type instanceof SchemaDate) {
return 'date';
}

if (type instanceof SchemaBuffer) {
return 'binData';
}

if (type instanceof SchemaObjectId) {
return 'objectId';
}

if (type instanceof SchemaDecimal128) {
return 'decimal';
}

if (type instanceof SchemaDouble) {
return 'double';
}

if (type instanceof Array) {
return 'array';
}

return null;
}

module.exports = {
inferBSONType
};
Loading

0 comments on commit a4b4299

Please sign in to comment.