Mongoose staging encryption tool not invoked after aggregation

I have a mongoose schema with the 'mongoose-encryption' plugin, for example:

let someSchema = new Schema({name, age});

someSchema.plugin(mongoose-encryption, {
        encryptionKey: 'eKey',
        signingKey: 'sKey',
        encryptedFields: ['age'],
        decryptPostSave: false
    });

      

After starting the model and repository, I tried to aggregate some query:

let aggregation = []; // just return all the docs.
someModel.aggregate(aggregation, (err, persons) => {
        return persons;
});

      

As a result I still get an encrypted age field, a little reading showed that the "post" method of the "init" event is not called after the aggregation (as described here - "Middleware Middleway Documents" ).

Is there a good solution? or any other workaround?

  • the data MUST be encrypted.

  • aggregation is also required (in real life - searching for another collection)

+3


source to share


1 answer


Since I didn't find a better answer, I changed my code (unfortunately, unfortunately) to decrypt the object myself - using the mongoose-encryption code to decrypt after the aggregation is complete.

Most of the code was taken from GitHub (so called decryptOne in my code): decryptSync function for mongoose encryption

The "trick" was to decrypt the internal search value - the internal document also has a "_ct" field that needs to be decrypted.

let lookup: { [innerField: string]: string[]; } = {
                    user: ['bio']
                };
this.decryptAggregation(aggregationResult, lookup);

      



After decryption, my function gets the dictionary of a known search collection and its required fields. In this example, another collection named by users and its encrypted field is his biography.

decryptAggregation(res: any[], innerLookup: { [innerField: string]: string[]; }) {
    for (let doc of res) {
        this.decryptSync(doc, innerLookup);
    }
}

private decryptSync(doc: any, innerLookup: { [innerField: string]: string[]; }) {
    this.decryptOne(doc, this.encryptedFields);
    for (let innerObj in innerLookup) {
        if (innerLookup.hasOwnProperty(innerObj)) {
            this.decryptOne(doc[innerObj], innerLookup[innerObj]);
        }
    }
};

private decryptOne(doc: any, fields: string[]) {
    let ct, ctWithIV, decipher, iv, idString, decryptedObject, decryptedObjectJSON, decipheredVal;

    if (doc._ct) {
        ctWithIV = doc._ct.hasOwnProperty('buffer') ? doc._ct.buffer : doc._ct;
        iv = ctWithIV.slice(this.VERSION_LENGTH, this.VERSION_LENGTH + this.IV_LENGTH);
        ct = ctWithIV.slice(this.VERSION_LENGTH + this.IV_LENGTH, ctWithIV.length);

        decipher = crypto.createDecipheriv(this.ENCRYPTION_ALGORITHM, this.encryptionKey, iv);
        try {
            decryptedObjectJSON = decipher.update(ct, undefined, 'utf8') + decipher.final('utf8');
            decryptedObject = JSON.parse(decryptedObjectJSON);
        } catch (err) {
            if (doc._id) {
                idString = doc._id.toString();
            } else {
                idString = 'unknown';
            }
            throw new Error('Error parsing JSON during decrypt of ' + idString + ': ' + err);
        }

        fields.forEach((field) => {
            decipheredVal = mpath.get(field, decryptedObject);

            //JSON.parse returns {type: "Buffer", data: Buffer} for Buffers
            //https://nodejs.org/api/buffer.html#buffer_buf_tojson
            if (_.isObject(decipheredVal) && decipheredVal.type === "Buffer") {
                this.setFieldValue(doc, field, decipheredVal.data);
            } else {
                this.setFieldValue(doc, field, decipheredVal);
            }
        });

        doc._ct = undefined;
        doc._ac = undefined;
    }
}

      

After this function I got my decrypted object, the last thing to do is return the desired fields to the client - using lodash.pick

+3


source







All Articles