mongoose-field-encryption
Advanced tools
Comparing version 1.0.1 to 1.1.0
@@ -1,13 +0,13 @@ | ||
'use strict'; | ||
"use strict"; | ||
const crypto = require('crypto'); | ||
const crypto = require("crypto"); | ||
const algorithm = 'aes-256-ctr'; | ||
const encryptedFieldNamePrefix = '__enc_'; | ||
const encryptedFieldDataSuffix = '_d'; | ||
const algorithm = "aes-256-ctr"; | ||
const encryptedFieldNamePrefix = "__enc_"; | ||
const encryptedFieldDataSuffix = "_d"; | ||
const encrypt = function(text, secret) { | ||
let cipher = crypto.createCipher(algorithm, secret) | ||
let crypted = cipher.update(text, 'utf8', 'hex') | ||
crypted += cipher.final('hex'); | ||
let cipher = crypto.createCipher(algorithm, secret); | ||
let crypted = cipher.update(text, "utf8", "hex"); | ||
crypted += cipher.final("hex"); | ||
return crypted; | ||
@@ -17,5 +17,5 @@ }; | ||
const decrypt = function(text, secret) { | ||
var decipher = crypto.createDecipher(algorithm, secret) | ||
var dec = decipher.update(text, 'hex', 'utf8') | ||
dec += decipher.final('utf8'); | ||
var decipher = crypto.createDecipher(algorithm, secret); | ||
var dec = decipher.update(text, "hex", "utf8"); | ||
dec += decipher.final("utf8"); | ||
return dec; | ||
@@ -25,5 +25,4 @@ }; | ||
const fieldEncryption = function(schema, options) { | ||
if (!options || !options.secret) { | ||
throw new Error('missing required secret'); | ||
throw new Error("missing required secret"); | ||
} | ||
@@ -34,2 +33,22 @@ | ||
// for mongoose 4/5 compatibility | ||
const defaultNext = function defaultNext(err) { | ||
if (err) { | ||
throw err; | ||
} | ||
}; | ||
function getCompatitibleNextFunc(next) { | ||
if (typeof next !== "function") { | ||
return defaultNext; | ||
} | ||
return next; | ||
} | ||
function getCompatibleData(next, data) { | ||
// in mongoose5, 'data' field is undefined | ||
if (!data) { | ||
return next; | ||
} | ||
return data; | ||
} | ||
// add marker fields to schema | ||
@@ -53,3 +72,4 @@ for (let field of fieldsToEncrypt) { | ||
if (!obj[encryptedFieldName] && fieldValue) { | ||
if (typeof fieldValue === 'string') { // handle strings separately to maintain searchability | ||
if (typeof fieldValue === "string") { | ||
// handle strings separately to maintain searchability | ||
const value = encrypt(fieldValue, secret); | ||
@@ -66,3 +86,3 @@ obj[field] = value; | ||
} | ||
}; | ||
} | ||
@@ -79,5 +99,5 @@ function decryptFields(obj, fields, secret) { | ||
obj[encryptedFieldName] = false; | ||
obj[encryptedFieldData] = ''; | ||
} else if (obj[encryptedFieldName]) { // handle strings separately to maintain searchability | ||
obj[encryptedFieldData] = ""; | ||
} else if (obj[encryptedFieldName]) { | ||
// handle strings separately to maintain searchability | ||
const encryptedValue = obj[field]; | ||
@@ -89,5 +109,7 @@ | ||
} | ||
}; | ||
} | ||
schema.pre('init', function(next, data) { | ||
schema.pre("init", function(_next, _data) { | ||
const next = getCompatitibleNextFunc(_next); | ||
const data = getCompatibleData(_next, _data); | ||
try { | ||
@@ -101,3 +123,5 @@ decryptFields(data, fieldsToEncrypt, secret); | ||
schema.pre('save', function(next) { | ||
schema.pre("save", function(_next) { | ||
const next = getCompatitibleNextFunc(_next); | ||
try { | ||
@@ -111,6 +135,5 @@ encryptFields(this, fieldsToEncrypt, secret); | ||
schema.pre('update', function(next) { | ||
schema.pre("update", function(_next) { | ||
const next = getCompatitibleNextFunc(_next); | ||
for (let field of fieldsToEncrypt) { | ||
let encryptedFieldName = encryptedFieldNamePrefix + field; | ||
@@ -121,3 +144,3 @@ let encryptedFieldValue = this._update.$set[encryptedFieldName]; | ||
if (encryptedFieldValue === false && plainTextValue) { | ||
if (typeof plainTextValue === 'string' || plainTextValue instanceof String) { | ||
if (typeof plainTextValue === "string" || plainTextValue instanceof String) { | ||
let updateObj = { $set: {} }; | ||
@@ -128,3 +151,5 @@ updateObj.$set[field] = encrypt(plainTextValue, secret); | ||
} else { | ||
return next(new Error('Cannot apply mongoose-field-encryption plugin on update to encrypt non string fields')); | ||
return next( | ||
new Error("Cannot apply mongoose-field-encryption plugin on update to encrypt non string fields") | ||
); | ||
} | ||
@@ -154,3 +179,2 @@ } | ||
}; | ||
}; | ||
@@ -157,0 +181,0 @@ |
{ | ||
"name": "mongoose-field-encryption", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "A simple symmetric encryption plugin for individual fields.", | ||
@@ -11,4 +11,4 @@ "main": "lib/mongoose-field-encryption.js", | ||
"test": "mocha", | ||
"coverage": "URI='mongodb://mfe:mfe@localhost/mongoose-field-encryption-test' istanbul cover ./node_modules/mocha/bin/_mocha", | ||
"coverage-report": "URI='mongodb://mfe:mfe@localhost/mongoose-field-encryption-test' istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" | ||
"coverage": "URI='mongodb://mfe:mfe@127.0.0.1:27017/mongoose-field-encryption-test' istanbul cover ./node_modules/mocha/bin/_mocha", | ||
"coverage-report": "URI='mongodb://mfe:mfe@127.0.0.1:27017/mongoose-field-encryption-test' istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage" | ||
}, | ||
@@ -41,13 +41,14 @@ "repository": { | ||
}, | ||
"dependencies": { | ||
"mongoose": "4.11.13" | ||
"peerDependencies": { | ||
"mongoose": ">=4.11.0" | ||
}, | ||
"devDependencies": { | ||
"bluebird": "3.5.0", | ||
"bluebird": "3.5.1", | ||
"chai": "4.1.2", | ||
"coveralls": "3.0.0", | ||
"coveralls": "3.0.2", | ||
"istanbul": "^0.4.5", | ||
"mocha": "3.5.3", | ||
"mocha-lcov-reporter": "1.3.0" | ||
"mocha": "5.2.0", | ||
"mocha-lcov-reporter": "1.3.0", | ||
"mongoose": "5.2.9" | ||
} | ||
} |
@@ -5,3 +5,3 @@ # mongoose-field-encryption | ||
A simple symmetric encryption plugin for individual fields. The goal of this plugin is to encrypt data but still allow searching over fields with string values. This plugin relies on the Node `crypto` module. Encryption and decryption happen transparently during save and find. | ||
A simple symmetric encryption plugin for individual fields. The goal of this plugin is to encrypt data but still allow searching over fields with string values. This plugin relies on the Node `crypto` module. Encryption and decryption happen transparently during save and find. | ||
@@ -14,3 +14,3 @@ As of the stable 1.0.0 release, this plugin works on individual fields of any type. However, note that for non-string fields, the original value is set to undefined after encryption. This is because if the schema has defined a field as an array, it would not be possible to replace it with a string value. | ||
Encryption is performed using `AES-256-CTR`. To encrypt, the relevant fields are encrypted with the provided secret and the resulting hex string is put in place of the actual value for `string` values. An extra `boolean` field with the prefix `__enc_` is added to the document which indicates if the provided field is encrypted or not. | ||
Encryption is performed using `AES-256-CTR`. To encrypt, the relevant fields are encrypted with the provided secret and the resulting hex string is put in place of the actual value for `string` values. An extra `boolean` field with the prefix `__enc_` is added to the document which indicates if the provided field is encrypted or not. | ||
@@ -36,9 +36,10 @@ Fields which are either objects or of a different type are converted to strings using `JSON.stringify` and the value stored in an extra marker field of type `string` with a naming scheme of `__enc_` as prefix and `_d` as suffix on the original field name. The original field is then set to `undefined`. Please note that this might break any custom validation and application of this plugin on non-string fields needs to be done with care. | ||
For example, given a schema as follows: | ||
```javascript | ||
let mongoose = require('mongoose'); | ||
let mongooseFieldEncryption = require('mongoose-field-encryption').fieldEncryption; | ||
let Schema = mongoose.Schema; | ||
let Post = new Schema({ | ||
title: String, | ||
```js | ||
const mongoose = require('mongoose'); | ||
const mongooseFieldEncryption = require('mongoose-field-encryption').fieldEncryption; | ||
const Schema = mongoose.Schema; | ||
const Post = new Schema({ | ||
title: String, | ||
message: String, | ||
@@ -55,3 +56,4 @@ references: { | ||
The resulting documents will have the following format: | ||
```javascript | ||
```js | ||
{ | ||
@@ -68,3 +70,3 @@ _id: ObjectId, | ||
`find` works transparently and you can make new documents as normal, but you should not use the `lean` option on a find if you want the fields of the document to be decrypted. `findOne`, `findById` and `save` also all work as normal. `update` works _only for string fields_ and you would also need to manually set the `__enc_` field value to false if you're updating an encrypted field. | ||
`find` works transparently and you can make new documents as normal, but you should not use the `lean` option on a find if you want the fields of the document to be decrypted. `findOne`, `findById` and `save` also all work as normal. `update` works _only for string fields_ and you would also need to manually set the `__enc_` field value to false if you're updating an encrypted field. | ||
@@ -83,2 +85,3 @@ From the mongoose package documentation: _Note that findAndUpdate/Remove do not execute any hooks or validation before making the change in the database. If you need hooks and validation, first query for the document and then save it._ | ||
For performance reasons, once the document has been encrypted, it remains so. The following methods are thus added to the schema: | ||
- `encryptFieldsSync()`: synchronous call that encrypts all fields as given by the plugin options | ||
@@ -94,13 +97,25 @@ - `decryptFieldsSync()`: synchronous call that decrypts encrypted fields as given by the plugin options | ||
```js | ||
const fieldEncryption = require('mongoose-field-encryption') | ||
const encrypted = fieldEncryption.encrypt('some text', 'secret')); | ||
const decrypted = fieldEncryption.decrypt(encrypted, 'secret')); // decrypted = 'some text' | ||
``` | ||
let fieldEncryption = require('mongoose-field-encryption') | ||
let encrypted = fieldEncryption.encrypt('some text', 'secret')); | ||
let decrypted = fieldEncryption.decrypt(encrypted, 'secret')); // decrypted = 'some text' | ||
``` | ||
## Testing | ||
0. Install dependencies with `npm install` and [install mongo](http://docs.mongodb.org/manual/installation/) if you don't have it yet. | ||
1. Start mongo with `mongod`. | ||
2. Run tests with `npm test`. Additionally you can pass your own mongodb uri as an environment variable if you would like to test against your own database, for e.g. `URI='mongodb://username:password@localhost/mongoose-field-encryption-test' npm test` | ||
1. Install dependencies with `npm install` and [install mongo](http://docs.mongodb.org/manual/installation/) if you don't have it yet. | ||
2. Start mongo with `mongod`. | ||
3. Run tests with `npm test`. Additionally you can pass your own mongodb uri as an environment variable if you would like to test against your own database, for e.g. `URI='mongodb://username:password@127.0.0.1:27017/mongoose-field-encryption-test' npm test` | ||
## Publishing | ||
- `npm version patch,minor,major` | ||
- `npm publish` | ||
## Changelog | ||
### 1.1.0 | ||
- Added support for mongoose 5 [https://github.com/victorparmar/mongoose-field-encryption/pull/16](https://github.com/victorparmar/mongoose-field-encryption/pull/16). | ||
- Removed mongoose dependency, moved to `peerDependencies`. | ||
- Formatted source code using prettier. |
13763
145
115
7
+ Added@mongodb-js/saslprep@1.2.0(transitive)
+ Added@types/webidl-conversions@7.0.3(transitive)
+ Added@types/whatwg-url@11.0.5(transitive)
+ Addedbson@6.10.3(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addedkareem@2.6.3(transitive)
+ Addedmemory-pager@1.5.0(transitive)
+ Addedmongodb@6.13.1(transitive)
+ Addedmongodb-connection-string-url@3.0.2(transitive)
+ Addedmongoose@8.11.0(transitive)
+ Addedmpath@0.9.0(transitive)
+ Addedmquery@5.0.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addedpunycode@2.3.1(transitive)
+ Addedsift@17.1.3(transitive)
+ Addedsparse-bitfield@3.0.3(transitive)
+ Addedtr46@5.0.0(transitive)
+ Addedwebidl-conversions@7.0.0(transitive)
+ Addedwhatwg-url@14.1.1(transitive)
- Removedmongoose@4.11.13
- Removedasync@2.1.4(transitive)
- Removedbluebird@2.10.2(transitive)
- Removedbson@1.0.9(transitive)
- Removedbuffer-shims@1.0.0(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removeddebug@2.6.8(transitive)
- Removedes6-promise@3.2.1(transitive)
- Removedhooks-fixed@2.0.0(transitive)
- Removedinherits@2.0.4(transitive)
- Removedisarray@1.0.0(transitive)
- Removedkareem@1.5.0(transitive)
- Removedlodash@4.17.21(transitive)
- Removedmongodb@2.2.31(transitive)
- Removedmongodb-core@2.1.15(transitive)
- Removedmongoose@4.11.13(transitive)
- Removedmpath@0.3.0(transitive)
- Removedmpromise@0.5.5(transitive)
- Removedmquery@2.3.1(transitive)
- Removedms@2.0.0(transitive)
- Removedmuri@1.2.2(transitive)
- Removedprocess-nextick-args@1.0.7(transitive)
- Removedreadable-stream@2.2.7(transitive)
- Removedregexp-clone@0.0.1(transitive)
- Removedrequire_optional@1.0.1(transitive)
- Removedresolve-from@2.0.0(transitive)
- Removedsafe-buffer@5.1.2(transitive)
- Removedsemver@5.7.2(transitive)
- Removedsliced@0.0.51.0.1(transitive)
- Removedstring_decoder@1.0.3(transitive)
- Removedutil-deprecate@1.0.2(transitive)