|
10 | 10 | import { MongoClient, GridFSBucket, Db } from 'mongodb';
|
11 | 11 | import { FilesAdapter, validateFilename } from './FilesAdapter';
|
12 | 12 | import defaults from '../../defaults';
|
| 13 | +const crypto = require('crypto'); |
13 | 14 |
|
14 | 15 | export class GridFSBucketAdapter extends FilesAdapter {
|
15 | 16 | _databaseURI: string;
|
16 | 17 | _connectionPromise: Promise<Db>;
|
17 | 18 | _mongoOptions: Object;
|
| 19 | + _algorithm: string; |
18 | 20 |
|
19 |
| - constructor(mongoDatabaseURI = defaults.DefaultMongoURI, mongoOptions = {}) { |
| 21 | + constructor( |
| 22 | + mongoDatabaseURI = defaults.DefaultMongoURI, |
| 23 | + mongoOptions = {}, |
| 24 | + fileKey = undefined |
| 25 | + ) { |
20 | 26 | super();
|
21 | 27 | this._databaseURI = mongoDatabaseURI;
|
22 |
| - |
| 28 | + this._algorithm = 'aes-256-gcm'; |
| 29 | + this._fileKey = |
| 30 | + fileKey !== undefined |
| 31 | + ? crypto |
| 32 | + .createHash('sha256') |
| 33 | + .update(String(fileKey)) |
| 34 | + .digest('base64') |
| 35 | + .substr(0, 32) |
| 36 | + : null; |
23 | 37 | const defaultMongoOptions = {
|
24 | 38 | useNewUrlParser: true,
|
25 | 39 | useUnifiedTopology: true,
|
@@ -51,7 +65,19 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
51 | 65 | const stream = await bucket.openUploadStream(filename, {
|
52 | 66 | metadata: options.metadata,
|
53 | 67 | });
|
54 |
| - await stream.write(data); |
| 68 | + if (this._fileKey !== null) { |
| 69 | + const iv = crypto.randomBytes(16); |
| 70 | + const cipher = crypto.createCipheriv(this._algorithm, this._fileKey, iv); |
| 71 | + const encryptedResult = Buffer.concat([ |
| 72 | + cipher.update(data), |
| 73 | + cipher.final(), |
| 74 | + iv, |
| 75 | + cipher.getAuthTag(), |
| 76 | + ]); |
| 77 | + await stream.write(encryptedResult); |
| 78 | + } else { |
| 79 | + await stream.write(data); |
| 80 | + } |
55 | 81 | stream.end();
|
56 | 82 | return new Promise((resolve, reject) => {
|
57 | 83 | stream.on('finish', resolve);
|
@@ -82,7 +108,24 @@ export class GridFSBucketAdapter extends FilesAdapter {
|
82 | 108 | chunks.push(data);
|
83 | 109 | });
|
84 | 110 | stream.on('end', () => {
|
85 |
| - resolve(Buffer.concat(chunks)); |
| 111 | + const data = Buffer.concat(chunks); |
| 112 | + if (this._fileKey !== null) { |
| 113 | + const authTagLocation = data.length - 16; |
| 114 | + const ivLocation = data.length - 32; |
| 115 | + const authTag = data.slice(authTagLocation); |
| 116 | + const iv = data.slice(ivLocation, authTagLocation); |
| 117 | + const encrypted = data.slice(0, ivLocation); |
| 118 | + const decipher = crypto.createDecipheriv( |
| 119 | + this._algorithm, |
| 120 | + this._fileKey, |
| 121 | + iv |
| 122 | + ); |
| 123 | + decipher.setAuthTag(authTag); |
| 124 | + return resolve( |
| 125 | + Buffer.concat([decipher.update(encrypted), decipher.final()]) |
| 126 | + ); |
| 127 | + } |
| 128 | + resolve(data); |
86 | 129 | });
|
87 | 130 | stream.on('error', (err) => {
|
88 | 131 | reject(err);
|
|
0 commit comments