Skip to content

Commit 0ce3174

Browse files
authored
Switch from fileKey to encryptionKey (#17)
* Switch from fileKey to encryptionKey * remove old notice * add default file location info and tests * add Dangefile to see if it reduces error * remove Dangerfile * add getFileLocation test * add missing testcase
1 parent cf6bc40 commit 0ce3174

File tree

5 files changed

+90
-43
lines changed

5 files changed

+90
-43
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pids
88
*.pid
99
*.seed
1010

11+
#Test file creation directory
12+
files
13+
1114
# Directory for instrumented libs generated by jscoverage/JSCover
1215
lib-cov
1316

README.md

+14-15
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,21 @@ When using parse-server-fs-adapter across multiple parse-server instances it's i
2424
"filesAdapter": {
2525
"module": "@parse/fs-files-adapter",
2626
"options": {
27-
"filesSubDirectory": "my/files/folder", // optional
28-
"fileKey": "someKey" //optional, but mandatory if you want to encrypt files
27+
"filesSubDirectory": "my/files/folder", // optional, defaults to ./files
28+
"encryptionKey": "someKey" //optional, but mandatory if you want to encrypt files
2929
}
3030
}
3131
}
3232
```
3333

3434
### Passing as an instance
35-
***Notice: If used with parse-server versions <= 4.2.0, DO NOT PASS in `PARSE_SERVER_FILE_KEY` or `fileKey` from parse-server. Instead pass your key directly to `FSFilesAdapter` using your own environment variable or hardcoding the string. parse-server versions > 4.2.0 can pass in `PARSE_SERVER_FILE_KEY` or `fileKey`.***
3635

3736
```javascript
3837
var FSFilesAdapter = require('@parse/fs-files-adapter');
3938

4039
var fsAdapter = new FSFilesAdapter({
41-
"filesSubDirectory": "my/files/folder", // optional
42-
"fileKey": "someKey" //optional, but mandatory if you want to encrypt files
40+
"filesSubDirectory": "my/files/folder", // optional, defaults to ./files
41+
"encryptionKey": "someKey" //optional, but mandatory if you want to encrypt files
4342
});
4443

4544
var api = new ParseServer({
@@ -49,16 +48,16 @@ var api = new ParseServer({
4948
})
5049
```
5150

52-
### Rotating to a new fileKey
53-
Periodically you may want to rotate your fileKey for security reasons. When this is the case, you can start up a development parse-server that has the same configuration as your production server. In the development server, initialize the file adapter with the new key and do the following in your `index.js`:
51+
### Rotating to a new encryptionKey
52+
Periodically you may want to rotate your encryptionKey for security reasons. When this is the case, you can start up a development parse-server that has the same configuration as your production server. In the development server, initialize the file adapter with the new key and do the following in your `index.js`:
5453

5554
#### Files were previously unencrypted and you want to encrypt
5655
```javascript
5756
var FSFilesAdapter = require('@parse/fs-files-adapter');
5857

5958
var fsAdapter = new FSFilesAdapter({
60-
"filesSubDirectory": "my/files/folder", // optional
61-
"fileKey": "newKey" //Use the newKey
59+
"filesSubDirectory": "my/files/folder", // optional, defaults to ./files
60+
"encryptionKey": "newKey" //Use the newKey
6261
});
6362

6463
var api = new ParseServer({
@@ -69,28 +68,28 @@ var api = new ParseServer({
6968

7069
//This can take awhile depending on how many files and how larger they are. It will attempt to rotate the key of all files in your filesSubDirectory
7170
//It is not recommended to do this on the production server, deploy a development server to complete the process.
72-
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey();
71+
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey();
7372
console.log('Files rotated to newKey: ' + rotated);
7473
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
7574
```
7675
77-
After successfully rotating your key, you should change the `fileKey` to `newKey` on your production server and then restart the server.
76+
After successfully rotating your key, you should change the `encryptionKey` to `newKey` on your production server and then restart the server.
7877
7978
8079
#### Files were previously encrypted with `oldKey` and you want to encrypt with `newKey`
81-
The same process as above, but pass in your `oldKey` to `rotateFileKey()`.
80+
The same process as above, but pass in your `oldKey` to `rotateEncryptionKey()`.
8281
```javascript
8382
//This can take awhile depending on how many files and how larger they are. It will attempt to rotate the key of all files in your filesSubDirectory
84-
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey({oldKey: oldKey});
83+
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey});
8584
console.log('Files rotated to newKey: ' + rotated);
8685
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
8786
```
8887

8988
#### Only rotate a select list of files that were previously encrypted with `oldKey` and you want to encrypt with `newKey`
90-
This is useful if for some reason there errors and some of the files werent rotated and returned in `notRotated`. The same process as above, but pass in your `oldKey` along with the array of `fileNames` to `rotateFileKey()`.
89+
This is useful if for some reason there errors and some of the files werent rotated and returned in `notRotated`. The same process as above, but pass in your `oldKey` along with the array of `fileNames` to `rotateEncryptionKey()`.
9190
```javascript
9291
//This can take awhile depending on how many files and how larger they are. It will attempt to rotate the key of all files in your filesSubDirectory
93-
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey({oldKey: oldKey, fileNames: ["fileName1.png","fileName2.png"]});
92+
const {rotated, notRotated} = await api.filesAdapter.rotateEncryptionKey({oldKey: oldKey, fileNames: ["fileName1.png","fileName2.png"]});
9493
console.log('Files rotated to newKey: ' + rotated);
9594
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
9695
```

index.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ const algorithm = 'aes-256-gcm';
1212

1313
function FileSystemAdapter(options) {
1414
options = options || {};
15-
this._fileKey = null;
15+
this._encryptionKey = null;
1616

17-
if (options.fileKey !== undefined){
18-
this._fileKey = crypto.createHash('sha256').update(String(options.fileKey)).digest('base64').substr(0, 32);
17+
if (options.encryptionKey !== undefined){
18+
this._encryptionKey = crypto.createHash('sha256').update(String(options.encryptionKey)).digest('base64').substr(0, 32);
1919
}
2020
let filesSubDirectory = options.filesSubDirectory || '';
2121
this._filesDir = filesSubDirectory;
@@ -30,11 +30,11 @@ FileSystemAdapter.prototype.createFile = function(filename, data) {
3030
const stream = fs.createWriteStream(filepath);
3131
return new Promise((resolve, reject) => {
3232
try{
33-
if(this._fileKey !== null){
33+
if(this._encryptionKey !== null){
3434
const iv = crypto.randomBytes(16);
3535
const cipher = crypto.createCipheriv(
3636
algorithm,
37-
this._fileKey,
37+
this._encryptionKey,
3838
iv
3939
);
4040
const encryptedResult = Buffer.concat([
@@ -96,14 +96,14 @@ FileSystemAdapter.prototype.getFileData = function(filename) {
9696
});
9797
stream.on('end', () => {
9898
const data = Buffer.concat(chunks);
99-
if(this._fileKey !== null){
99+
if(this._encryptionKey !== null){
100100
const authTagLocation = data.length - 16;
101101
const ivLocation = data.length - 32;
102102
const authTag = data.slice(authTagLocation);
103103
const iv = data.slice(ivLocation,authTagLocation);
104104
const encrypted = data.slice(0,ivLocation);
105105
try{
106-
const decipher = crypto.createDecipheriv(algorithm, this._fileKey, iv);
106+
const decipher = crypto.createDecipheriv(algorithm, this._encryptionKey, iv);
107107
decipher.setAuthTag(authTag);
108108
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
109109
return resolve(decrypted);
@@ -119,12 +119,12 @@ FileSystemAdapter.prototype.getFileData = function(filename) {
119119
});
120120
}
121121

122-
FileSystemAdapter.prototype.rotateFileKey = function(options = {}) {
122+
FileSystemAdapter.prototype.rotateEncryptionKey = function(options = {}) {
123123
const applicationDir = this._getApplicationDir();
124124
var fileNames = [];
125125
var oldKeyFileAdapter = {};
126126
if (options.oldKey !== undefined) {
127-
oldKeyFileAdapter = new FileSystemAdapter({filesSubDirectory: this._filesDir, fileKey: options.oldKey});
127+
oldKeyFileAdapter = new FileSystemAdapter({filesSubDirectory: this._filesDir, encryptionKey: options.oldKey});
128128
}else{
129129
oldKeyFileAdapter = new FileSystemAdapter({filesSubDirectory: this._filesDir});
130130
}

spec/secureFiles.spec.js

+58-19
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,49 @@ describe('File encryption tests', () => {
1919
})
2020
});
2121

22-
it("should save encrypted file", async function(done) {
22+
it('should create file location based on config', async function(done) {
23+
var fsAdapter = new FileSystemAdapter();
24+
var config = {mount: '/parse', applicationId: 'yolo'}
25+
let location = fsAdapter.getFileLocation(config, 'hello.txt')
26+
expect(location).toBe('/parse/files/yolo/hello.txt');
27+
done()
28+
}, 5000)
29+
30+
it("should save encrypted file in default directory", async function(done) {
31+
var adapter = new FileSystemAdapter({
32+
encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
33+
});
34+
var filename = 'file2.txt';
35+
const filePath = 'files/'+filename;
36+
await adapter.createFile(filename, "hello world", 'text/utf8');
37+
const result = await adapter.getFileData(filename);
38+
expect(result instanceof Buffer).toBe(true);
39+
expect(result.toString('utf-8')).toEqual("hello world");
40+
const data = fs.readFileSync(filePath);
41+
expect(data.toString('utf-8')).not.toEqual("hello world");
42+
done()
43+
}, 5000);
44+
45+
it("should save encrypted file in specified directory", async function(done) {
2346
var adapter = new FileSystemAdapter({
2447
filesSubDirectory: directory,
25-
fileKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
48+
encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
49+
});
50+
var filename = 'file2.txt';
51+
const filePath = 'files/'+directory+'/'+filename;
52+
await adapter.createFile(filename, "hello world", 'text/utf8');
53+
const result = await adapter.getFileData(filename);
54+
expect(result instanceof Buffer).toBe(true);
55+
expect(result.toString('utf-8')).toEqual("hello world");
56+
const data = fs.readFileSync(filePath);
57+
expect(data.toString('utf-8')).not.toEqual("hello world");
58+
done()
59+
}, 5000);
60+
61+
it("should save encrypted file in specified directory when directory starts with /", async function(done) {
62+
var adapter = new FileSystemAdapter({
63+
filesSubDirectory: '/sub1/sub2',
64+
encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
2665
});
2766
var filename = 'file2.txt';
2867
const filePath = 'files/'+directory+'/'+filename;
@@ -41,7 +80,7 @@ describe('File encryption tests', () => {
4180
});
4281
const encryptedAdapter = new FileSystemAdapter({
4382
filesSubDirectory: directory,
44-
fileKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
83+
encryptionKey: '89E4AFF1-DFE4-4603-9574-BFA16BB446FD'
4584
});
4685
const fileName1 = 'file1.txt';
4786
const data1 = "hello world";
@@ -61,7 +100,7 @@ describe('File encryption tests', () => {
61100
expect(result.toString('utf-8')).toEqual(data2);
62101
const unEncryptedData2 = fs.readFileSync(filePath2);
63102
//Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
64-
const {rotated, notRotated} = await encryptedAdapter.rotateFileKey();
103+
const {rotated, notRotated} = await encryptedAdapter.rotateEncryptionKey();
65104
expect(rotated.length).toEqual(2);
66105
expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
67106
expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
@@ -80,14 +119,14 @@ describe('File encryption tests', () => {
80119
}, 5000);
81120

82121
it("should rotate key of all old encrypted files to files encrypted with a new key", async function(done) {
83-
const oldFileKey = 'oldKeyThatILoved';
122+
const oldEncryptionKey = 'oldKeyThatILoved';
84123
const oldEncryptedAdapter = new FileSystemAdapter({
85124
filesSubDirectory: directory,
86-
fileKey: oldFileKey
125+
encryptionKey: oldEncryptionKey
87126
});
88127
const encryptedAdapter = new FileSystemAdapter({
89128
filesSubDirectory: directory,
90-
fileKey: 'newKeyThatILove'
129+
encryptionKey: 'newKeyThatILove'
91130
});
92131
const fileName1 = 'file1.txt';
93132
const data1 = "hello world";
@@ -107,7 +146,7 @@ describe('File encryption tests', () => {
107146
expect(result.toString('utf-8')).toEqual(data2);
108147
const oldEncryptedData2 = fs.readFileSync(filePath2);
109148
//Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
110-
const {rotated, notRotated} = await encryptedAdapter.rotateFileKey({oldKey: oldFileKey});
149+
const {rotated, notRotated} = await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
111150
expect(rotated.length).toEqual(2);
112151
expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
113152
expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
@@ -126,10 +165,10 @@ describe('File encryption tests', () => {
126165
}, 5000);
127166

128167
it("should rotate key of all old encrypted files to unencrypted files", async function(done) {
129-
const oldFileKey = 'oldKeyThatILoved';
168+
const oldEncryptionKey = 'oldKeyThatILoved';
130169
const oldEncryptedAdapter = new FileSystemAdapter({
131170
filesSubDirectory: directory,
132-
fileKey: oldFileKey
171+
encryptionKey: oldEncryptionKey
133172
});
134173
const unEncryptedAdapter = new FileSystemAdapter({
135174
filesSubDirectory: directory
@@ -152,7 +191,7 @@ describe('File encryption tests', () => {
152191
expect(result.toString('utf-8')).toEqual(data2);
153192
const oldEncryptedData2 = fs.readFileSync(filePath2);
154193
//Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter
155-
const {rotated, notRotated} = await unEncryptedAdapter.rotateFileKey({oldKey: oldFileKey});
194+
const {rotated, notRotated} = await unEncryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
156195
expect(rotated.length).toEqual(2);
157196
expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
158197
expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
@@ -171,14 +210,14 @@ describe('File encryption tests', () => {
171210
}, 5000);
172211

173212
it("should only encrypt specified fileNames with the new key", async function(done) {
174-
const oldFileKey = 'oldKeyThatILoved';
213+
const oldEncryptionKey = 'oldKeyThatILoved';
175214
const oldEncryptedAdapter = new FileSystemAdapter({
176215
filesSubDirectory: directory,
177-
fileKey: oldFileKey
216+
encryptionKey: oldEncryptionKey
178217
});
179218
const encryptedAdapter = new FileSystemAdapter({
180219
filesSubDirectory: directory,
181-
fileKey: 'newKeyThatILove'
220+
encryptionKey: 'newKeyThatILove'
182221
});
183222
const unEncryptedAdapter = new FileSystemAdapter({
184223
filesSubDirectory: directory
@@ -205,7 +244,7 @@ describe('File encryption tests', () => {
205244
const data3 = "hello past world";
206245
await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8');
207246
//Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
208-
const {rotated, notRotated} = await encryptedAdapter.rotateFileKey({oldKey: oldFileKey, fileNames: [fileName1,fileName2]});
247+
const {rotated, notRotated} = await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey, fileNames: [fileName1,fileName2]});
209248
expect(rotated.length).toEqual(2);
210249
expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
211250
expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);
@@ -225,14 +264,14 @@ describe('File encryption tests', () => {
225264
}, 5000);
226265

227266
it("should return fileNames of those it can't encrypt with the new key", async function(done) {
228-
const oldFileKey = 'oldKeyThatILoved';
267+
const oldEncryptionKey = 'oldKeyThatILoved';
229268
const oldEncryptedAdapter = new FileSystemAdapter({
230269
filesSubDirectory: directory,
231-
fileKey: oldFileKey
270+
encryptionKey: oldEncryptionKey
232271
});
233272
const encryptedAdapter = new FileSystemAdapter({
234273
filesSubDirectory: directory,
235-
fileKey: 'newKeyThatILove'
274+
encryptionKey: 'newKeyThatILove'
236275
});
237276
const unEncryptedAdapter = new FileSystemAdapter({
238277
filesSubDirectory: directory
@@ -263,7 +302,7 @@ describe('File encryption tests', () => {
263302
expect(result instanceof Buffer).toBe(true);
264303
expect(result.toString('utf-8')).toEqual(data3);
265304
//Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter
266-
const {rotated, notRotated} = await encryptedAdapter.rotateFileKey({oldKey: oldFileKey});
305+
const {rotated, notRotated} = await encryptedAdapter.rotateEncryptionKey({oldKey: oldEncryptionKey});
267306
expect(rotated.length).toEqual(2);
268307
expect(rotated.filter(function(value){ return value === fileName1;}).length).toEqual(1);
269308
expect(rotated.filter(function(value){ return value === fileName2;}).length).toEqual(1);

spec/test.spec.js

+6
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ describe('FileSystemAdapter tests', () => {
1010

1111
filesAdapterTests.testAdapter("FileSystemAdapter", fsAdapter);
1212
})
13+
14+
describe('FileSystemAdapter tests - no options', () => {
15+
var fsAdapter = new FileSystemAdapter();
16+
17+
filesAdapterTests.testAdapter("FileSystemAdapter", fsAdapter);
18+
})

0 commit comments

Comments
 (0)