Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@parse/fs-files-adapter

Package Overview
Dependencies
Maintainers
3
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@parse/fs-files-adapter - npm Package Compare versions

Comparing version 1.0.1 to 1.1.0

.nycrc

4

CHANGELOG.md
# Change Log
## [1.1.0](https://github.com/parse-server-modules/parse-server-fs-adapter/tree/1.1.0) (2020-10-21)
- Added AES encryption/decryption using native crypto [\#15](https://github.com/parse-community/parse-server-fs-adapter/pull/15) (thanks to [Corey](https://github.com/cbaker6))
## [1.0.1](https://github.com/parse-server-modules/parse-server-fs-adapter/tree/1.0.1) (2016-08-15)

@@ -4,0 +8,0 @@ **Closed issues:**

@@ -10,5 +10,12 @@ 'use strict';

var pathSep = require('path').sep;
const crypto = require("crypto");
const algorithm = 'aes-256-gcm';
function FileSystemAdapter(options) {
options = options || {};
this._fileKey = null;
if (options.fileKey !== undefined){
this._fileKey = crypto.createHash('sha256').update(String(options.fileKey)).digest('base64').substr(0, 32);
}
let filesSubDirectory = options.filesSubDirectory || '';

@@ -23,10 +30,34 @@ this._filesDir = filesSubDirectory;

FileSystemAdapter.prototype.createFile = function(filename, data) {
let filepath = this._getLocalFilePath(filename);
const stream = fs.createWriteStream(filepath);
return new Promise((resolve, reject) => {
let filepath = this._getLocalFilePath(filename);
fs.writeFile(filepath, data, (err) => {
if(err !== null) {
return reject(err);
}
resolve(data);
});
try{
if(this._fileKey !== null){
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(
algorithm,
this._fileKey,
iv
);
const encryptedResult = Buffer.concat([
cipher.update(data),
cipher.final(),
iv,
cipher.getAuthTag(),
]);
stream.write(encryptedResult);
stream.end();
stream.on('finish', function() {
resolve(data);
});
}else{
stream.write(data);
stream.end();
stream.on('finish', function() {
resolve(data);
});
}
}catch(err){
return reject(err);
}
});

@@ -36,11 +67,15 @@ }

FileSystemAdapter.prototype.deleteFile = function(filename) {
let filepath = this._getLocalFilePath(filename);
const chunks = [];
const stream = fs.createReadStream(filepath);
return new Promise((resolve, reject) => {
let filepath = this._getLocalFilePath(filename);
fs.readFile( filepath , function (err, data) {
if(err !== null) {
return reject(err);
}
fs.unlink(filepath, (unlinkErr) => {
if(err !== null) {
return reject(unlinkErr);
stream.read();
stream.on('data', (data) => {
chunks.push(data);
});
stream.on('end', () => {
const data = Buffer.concat(chunks);
fs.unlink(filepath, (err) => {
if(err !== null) {
return reject(err);
}

@@ -50,3 +85,5 @@ resolve(data);

});
stream.on('error', (err) => {
reject(err);
});
});

@@ -56,13 +93,86 @@ }

FileSystemAdapter.prototype.getFileData = function(filename) {
let filepath = this._getLocalFilePath(filename);
const stream = fs.createReadStream(filepath);
stream.read();
return new Promise((resolve, reject) => {
let filepath = this._getLocalFilePath(filename);
fs.readFile( filepath , function (err, data) {
if(err !== null) {
return reject(err);
const chunks = [];
stream.on('data', (data) => {
chunks.push(data);
});
stream.on('end', () => {
const data = Buffer.concat(chunks);
if(this._fileKey !== null){
const authTagLocation = data.length - 16;
const ivLocation = data.length - 32;
const authTag = data.slice(authTagLocation);
const iv = data.slice(ivLocation,authTagLocation);
const encrypted = data.slice(0,ivLocation);
try{
const decipher = crypto.createDecipheriv(algorithm, this._fileKey, iv);
decipher.setAuthTag(authTag);
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
return resolve(decrypted);
}catch(err){
return reject(err);
}
}
resolve(data);
});
stream.on('error', (err) => {
reject(err);
});
});
}
FileSystemAdapter.prototype.rotateFileKey = function(options = {}) {
const applicationDir = this._getApplicationDir();
var fileNames = [];
var oldKeyFileAdapter = {};
if (options.oldKey !== undefined) {
oldKeyFileAdapter = new FileSystemAdapter({filesSubDirectory: this._filesDir, fileKey: options.oldKey});
}else{
oldKeyFileAdapter = new FileSystemAdapter({filesSubDirectory: this._filesDir});
}
if (options.fileNames !== undefined){
fileNames = options.fileNames;
}else{
fileNames = fs.readdirSync(applicationDir);
fileNames = fileNames.filter(fileName => fileName.indexOf('.') !== 0);
}
return new Promise((resolve, _reject) => {
var fileNamesNotRotated = fileNames;
var fileNamesRotated = [];
var fileNameTotal = fileNames.length;
var fileNameIndex = 0;
fileNames.forEach(fileName => {
oldKeyFileAdapter
.getFileData(fileName)
.then(plainTextData => {
//Overwrite file with data encrypted with new key
this.createFile(fileName, plainTextData)
.then(() => {
fileNamesRotated.push(fileName);
fileNamesNotRotated = fileNamesNotRotated.filter(function(value){ return value !== fileName;})
fileNameIndex += 1;
if (fileNameIndex == fileNameTotal){
resolve({rotated: fileNamesRotated, notRotated: fileNamesNotRotated});
}
})
.catch(() => {
fileNameIndex += 1;
if (fileNameIndex == fileNameTotal){
resolve({rotated: fileNamesRotated, notRotated: fileNamesNotRotated});
}
})
})
.catch(() => {
fileNameIndex += 1;
if (fileNameIndex == fileNameTotal){
resolve({rotated: fileNamesRotated, notRotated: fileNamesNotRotated});
}
});
});
});
}
FileSystemAdapter.prototype.getFileLocation = function(config, filename) {

@@ -69,0 +179,0 @@ return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);

14

package.json
{
"name": "@parse/fs-files-adapter",
"version": "1.0.1",
"version": "1.1.0",
"description": "File system adapter for parse-server",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/parse-community/parse-server-fs-adapter"
},
"scripts": {
"test": "istanbul cover -x **/spec/** jasmine --captureExceptions"
"test": "jasmine",
"coverage": "nyc jasmine"
},

@@ -18,7 +23,6 @@ "keywords": [

"devDependencies": {
"codecov": "^1.0.1",
"istanbul": "^0.4.2",
"jasmine": "^2.4.1",
"nyc": "^15.1.0",
"jasmine": "^3.5.0",
"parse-server-conformance-tests": "^1.0.0"
}
}

@@ -5,14 +5,17 @@ # parse-server-fs-adapter

parse-server file system storage adapter
parse-server file system storage adapter.
# installation
# Multiple instances of parse-server
When using parse-server-fs-adapter across multiple parse-server instances it's important to establish "centralization" of your file storage (this is the same premise as the other file adapters, you are sending/recieving files through a dedicated link). You can accomplish this at the file storage level by Samba mounting (or any other type of mounting) your storage to each of your parse-server instances, e.g if you are using parse-server via docker (volume mount your SMB drive to `- /Volumes/SMB-Drive/MyParseApp1/files:/parse-server/files`). All parse-server instances need to be able to read and write to the same storage in order for parse-server-fs-adapter to work properly with parse-server. If the file storage isn't centralized, parse-server will have trouble locating files and you will get random behavior on client-side.
# Installation
`npm install --save @parse/fs-files-adapter`
# usage with parse-server
# Usage with parse-server
### using a config file
### Using a config file
```
```javascript
{

@@ -25,3 +28,4 @@ "appId": 'my_app_id',

"options": {
"filesSubDirectory": "my/files/folder" // optional
"filesSubDirectory": "my/files/folder", // optional
"fileKey": "someKey" //optional, but mandatory if you want to encrypt files
}

@@ -32,10 +36,31 @@ }

### passing as an instance
### Passing as an instance
***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`.***
```javascript
var FSFilesAdapter = require('@parse/fs-files-adapter');
var fsAdapter = new FSFilesAdapter({
"filesSubDirectory": "my/files/folder", // optional
"fileKey": "someKey" //optional, but mandatory if you want to encrypt files
});
var api = new ParseServer({
appId: 'my_app',
masterKey: 'master_key',
filesAdapter: fsAdapter
})
```
### Rotating to a new fileKey
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`:
#### Files were previously unencrypted and you want to encrypt
```javascript
var FSFilesAdapter = require('@parse/fs-files-adapter');
var fsAdapter = new FSFilesAdapter({
"filesSubDirectory": "my/files/folder" // optional
});
"filesSubDirectory": "my/files/folder", // optional
"fileKey": "newKey" //Use the newKey
});

@@ -47,3 +72,29 @@ var api = new ParseServer({

})
//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
//It is not recommended to do this on the production server, deploy a development server to complete the process.
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey();
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
```
After successfully rotating your key, you should change the `fileKey` to `newKey` on your production server and then restart the server.
#### Files were previously encrypted with `oldKey` and you want to encrypt with `newKey`
The same process as above, but pass in your `oldKey` to `rotateFileKey()`.
```javascript
//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
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey({oldKey: oldKey});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
```
#### Only rotate a select list of files that were previously encrypted with `oldKey` and you want to encrypt with `newKey`
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()`.
```javascript
//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
const {rotated, notRotated} = await api.filesAdapter.rotateFileKey({oldKey: oldKey, fileNames: ["fileName1.png","fileName2.png"]});
console.log('Files rotated to newKey: ' + rotated);
console.log('Files that couldn't be rotated to newKey: ' + notRotated);
```
{
"spec_dir": "spec",
"spec_files": [
"test.spec.js"
"test.spec.js",
"secureFiles.spec.js"
]
}

@@ -11,3 +11,3 @@ 'use strict';

filesAdapterTests.testAdapter("FileSystemAdapter", fsAdapter);
filesAdapterTests.testAdapter("FileSystemAdapter", fsAdapter);
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc