Comparing version 0.2.5 to 0.3.0
{ | ||
"name": "fpark", | ||
"version": "0.2.5", | ||
"version": "0.3.0", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -62,2 +62,11 @@ # Fpark | ||
### Systemd | ||
- Install | ||
``` | ||
cd /tmp | ||
curl https://raw.githubusercontent.com/Ideolys/fpark/master/install.sh | sudo bash | ||
``` | ||
## Usage | ||
@@ -100,3 +109,3 @@ | ||
#### GET /file/:container/:filename | ||
#### GET /file/:filename/container/:container | ||
@@ -110,5 +119,6 @@ The url is **public**. | ||
Query options for the url are: | ||
- `access_key` : access key to get a file for a container. It is mandatory. The key is given at the creation of the container (see Container creation). | ||
- `size` : a valid size in `config.SIZES` to resize on the fly a file of type image. | ||
#### PUT /file/:container/:filename | ||
#### PUT /file/:filename/container/:container | ||
@@ -119,3 +129,3 @@ Put a file with id `filename` to a container `container`. | ||
#### DELETE /file/:container/:filename | ||
#### DELETE /file/:filename/container/:container | ||
@@ -135,3 +145,4 @@ Delete a file given by `filename` from a container `container`. | ||
"container" : "a unique key", | ||
"key" : "public key" | ||
"key" : "public key", | ||
"accessKey" : "a key to access GET /file/:filename" | ||
} | ||
@@ -146,3 +157,3 @@ ``` | ||
1. Register a container by calling the API `POST /node/register` **or** put the public key of the container in the keys directory as `container.pub` where `container` is the name of the container to create. | ||
1. Register a container by calling the API `POST /node/register` **or** put the public key of the container in the keys directory as `container.pub` where `container` is the name of the container to create and set an access key for the container in a file as `contaiener.access_key`. | ||
1. Create a JsonWebToken token with the field `aud` equals to the registered `container`. | ||
@@ -149,0 +160,0 @@ 1. Add the token in the header `authorization` as `Authorization: Bearer <token>`. |
@@ -19,2 +19,5 @@ const fs = require('fs'); | ||
const kittenLogger = require('kitten-logger'); | ||
const logger = kittenLogger.createPersistentLogger('del_file'); | ||
/** | ||
@@ -34,2 +37,3 @@ * DEL API | ||
if (getHeaderNthNode(req.headers) === 3) { | ||
logger.warn({ msg : 'Depth reached', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
@@ -44,2 +48,3 @@ } | ||
let proxy = proxyFactory(() => { | ||
logger.warn({ msg : 'cannot proxy' }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
@@ -58,2 +63,3 @@ }); | ||
}, () => { | ||
logger.warn({ msg : 'Cannot proxy' }, { idKittenLogger : req.log_id }); | ||
respond(res, 500); | ||
@@ -67,2 +73,3 @@ }); | ||
if (err) { | ||
logger.warn({ msg : 'cannot delete', err }, { idKittenLogger : req.log_id }); | ||
return respond(res, 404); | ||
@@ -69,0 +76,0 @@ } |
@@ -26,2 +26,6 @@ | ||
const kittenLogger = require('kitten-logger'); | ||
const auth = require('../commons/auth'); | ||
const logger = kittenLogger.createPersistentLogger('get_file'); | ||
/** | ||
@@ -33,2 +37,3 @@ * Get a file (initialize streams) | ||
* @param {Object} params request's params | ||
* @param {Object} queryParams query parameters | ||
* @param {String} keyNodes path 'node1-node2-node3' | ||
@@ -38,5 +43,6 @@ * @param {Array} streams | ||
*/ | ||
function getFile (CONFIG, req, res, params, keyNodes, streams, handler) { | ||
function getFile (CONFIG, req, res, params, queryParams, keyNodes, streams, handler) { | ||
function handlerError (err) { | ||
if (getHeaderNthNode(req.headers) === 3 || getHeaderFromNode(req.headers)) { | ||
logger.warn({ msg : 'Depth reached', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
return respond(res, 404); | ||
@@ -52,11 +58,7 @@ } | ||
if (isImage(extensionWithoutDot) && req.url) { | ||
let query = url.parse(req.url).search; | ||
let sizeId = queryParams.get('size'); | ||
let size = getSize(CONFIG, sizeId); | ||
if (query) { | ||
let sizeId = new URLSearchParams(url.parse(req.url).search).get('size'); | ||
let size = getSize(CONFIG, sizeId); | ||
if (size) { | ||
streams.unshift(resize(CONFIG, sizeId)); | ||
} | ||
if (size) { | ||
streams.unshift(resize(CONFIG, sizeId)); | ||
} | ||
@@ -80,85 +82,90 @@ } | ||
exports.getApi = function getApi (req, res, params, store) { | ||
let nodes = repartition.getNodesToPersistTo(params.id, store.CONFIG.NODES, store.CONFIG.REPLICATION_NB_REPLICAS); | ||
let isAllowedToWrite = repartition.isCurrentNodeInPersistentNodes(nodes, store.CONFIG.ID); | ||
auth.verifyAccessKey(req, res, params, queryParams => { | ||
let nodes = repartition.getNodesToPersistTo(params.id, store.CONFIG.NODES, store.CONFIG.REPLICATION_NB_REPLICAS); | ||
let isAllowedToWrite = repartition.isCurrentNodeInPersistentNodes(nodes, store.CONFIG.ID); | ||
if (!isAllowedToWrite && nodes.length) { | ||
if (getHeaderNthNode(req.headers) === 3) { | ||
return respond(res, 404); | ||
} | ||
return queue(nodes, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
if (!isAllowedToWrite && nodes.length) { | ||
if (getHeaderNthNode(req.headers) === 3) { | ||
logger.warn({ msg : 'Depth reached', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
return respond(res, 404); | ||
} | ||
let proxy = proxyFactory(() => { | ||
return respond(res, 404); | ||
}); | ||
return queue(nodes, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
} | ||
let headers = {}; | ||
setHeaderNthNode(headers, req.headers); | ||
setHeaderCurrentNode(headers, store.CONFIG.ID); | ||
let proxy = proxyFactory(() => { | ||
return respond(res, 404); | ||
}); | ||
proxy(req, res, { | ||
selfHandleResponse : true, | ||
target : node.host, | ||
headers | ||
let headers = {}; | ||
setHeaderNthNode(headers, req.headers); | ||
setHeaderCurrentNode(headers, store.CONFIG.ID); | ||
proxy(req, res, { | ||
selfHandleResponse : true, | ||
target : node.host, | ||
headers | ||
}); | ||
}, () => { | ||
logger.warn({ msg : 'Cannot proxy', }, { idKittenLogger : req.log_id }); | ||
respond(res, 500); | ||
}); | ||
}, () => { | ||
respond(res, 500); | ||
}); | ||
} | ||
} | ||
let keyNodes = repartition.flattenNodes(nodes); | ||
let keyNodes = repartition.flattenNodes(nodes); | ||
res.setHeader('Cache-Control', 'max-age=' + store.CONFIG.CACHE_CONTROL_MAX_AGE + ',immutable'); | ||
res.setHeader('Content-Encoding', 'gzip'); | ||
res.setHeader('Cache-Control', 'max-age=' + store.CONFIG.CACHE_CONTROL_MAX_AGE + ',immutable'); | ||
res.setHeader('Content-Encoding', 'gzip'); | ||
getFile(store.CONFIG, req, res, params, keyNodes, [zlib.createGzip()], () => { | ||
if (!nodes.length) { | ||
return respond(res, 404); | ||
} | ||
let headers = { | ||
'accept-encoding' : 'gzip' | ||
}; | ||
setHeaderCurrentNode(headers, store.CONFIG.ID); | ||
// Try to get file from another node | ||
// Save it | ||
// Serve it | ||
queue(nodes, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
getFile(store.CONFIG, req, res, params, queryParams, keyNodes, [zlib.createGzip()], () => { | ||
if (!nodes.length) { | ||
return respond(res, 404); | ||
} | ||
setHeaderNthNode(headers); | ||
let _req = { | ||
method : 'GET', | ||
headers, | ||
let headers = { | ||
'accept-encoding' : 'gzip' | ||
}; | ||
setHeaderCurrentNode(headers, store.CONFIG.ID); | ||
fetch(node.host + req.url, _req).then((resRequest) => { | ||
if (resRequest.status !== 200) { | ||
// Try to get file from another node | ||
// Save it | ||
// Serve it | ||
queue(nodes, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
} | ||
putFile(resRequest.body, params, store, keyNodes, null, err => { | ||
if (err) { | ||
return respond(res, 500); | ||
setHeaderNthNode(headers); | ||
let _req = { | ||
method : 'GET', | ||
headers, | ||
}; | ||
fetch(node.host + req.url, _req).then((resRequest) => { | ||
if (resRequest.status !== 200) { | ||
return next(); | ||
} | ||
getFile(store.CONFIG, _req, res, params, keyNodes, [], () => { | ||
respond(res, 404); | ||
putFile(resRequest.body, params, store, keyNodes, null, err => { | ||
if (err) { | ||
logger.warn({ msg : 'Cannot get file', err }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
} | ||
getFile(store.CONFIG, _req, res, params, queryParams, keyNodes, [], () => { | ||
respond(res, 404); | ||
}); | ||
}); | ||
}).catch(() => { | ||
next(); | ||
}); | ||
}).catch(() => { | ||
next(); | ||
}, () => { | ||
// No file has been found | ||
respond(res, 404); | ||
}); | ||
}, () => { | ||
// No file has been found | ||
respond(res, 404); | ||
}); | ||
}); | ||
} |
@@ -12,5 +12,5 @@ const { putApi } = require('./put'); | ||
function load (router, store) { | ||
router.on('GET' , '/file/container/:containerId/:id', getApi, store); | ||
router.on('PUT' , '/file/container/:containerId/:id', putApi, store); | ||
router.on('DELETE', '/file/container/:containerId/:id', delApi, store); | ||
router.on('GET' , '/file/:id/container/:containerId', getApi, store); | ||
router.on('PUT' , '/file/:id/container/:containerId', putApi, store); | ||
router.on('DELETE', '/file/:id/container/:containerId', delApi, store); | ||
@@ -17,0 +17,0 @@ if (store.CONFIG.IS_REGISTRATION_ENABLED) { |
@@ -25,3 +25,3 @@ const fs = require('fs'); | ||
if (!_body.container || !_body.key) { | ||
if (!_body.container || !_body.key || !_body.accessKey) { | ||
return respond(res, 400); | ||
@@ -39,36 +39,42 @@ } | ||
setKey(_body.container, _body.key); | ||
fs.writeFile(path.join(store.CONFIG.KEYS_DIRECTORY, _body.container + '.access_key'), _body.accessKey, { flag : 'wx' }, (err) => { | ||
if (err) { | ||
return respond(res, 500); | ||
} | ||
if (getHeaderFromNode(req.headers)) { | ||
return respond(res, 200); | ||
} | ||
setKey(_body.container, _body.key); | ||
queue(store.CONFIG.NODES, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
if (getHeaderFromNode(req.headers)) { | ||
return respond(res, 200); | ||
} | ||
let headers = { | ||
'Content-Type' : 'application/json' | ||
}; | ||
setHeaderCurrentNode(headers); | ||
queue(store.CONFIG.NODES, (node, next) => { | ||
if (node.id === store.CONFIG.ID) { | ||
return next(); | ||
} | ||
let _req = { | ||
method : 'POST', | ||
headers, | ||
body | ||
}; | ||
let headers = { | ||
'Content-Type' : 'application/json' | ||
}; | ||
setHeaderCurrentNode(headers); | ||
fetch(node.host + req.url, _req).then((resRequest) => { | ||
if (resRequest.status !== 200) { | ||
let _req = { | ||
method : 'POST', | ||
headers, | ||
body | ||
}; | ||
fetch(node.host + req.url, _req).then((resRequest) => { | ||
if (resRequest.status !== 200) { | ||
return respond(res, 500); | ||
} | ||
next(); | ||
}).catch(() => { | ||
return respond(res, 500); | ||
} | ||
next(); | ||
}).catch(() => { | ||
return respond(res, 500); | ||
}); | ||
}, () => { | ||
// No file has been found | ||
respond(res, 200); | ||
}); | ||
}, () => { | ||
// No file has been found | ||
respond(res, 200); | ||
}); | ||
@@ -75,0 +81,0 @@ }); |
@@ -32,2 +32,5 @@ const fs = require('fs'); | ||
const kittenLogger = require('kitten-logger'); | ||
const logger = kittenLogger.createPersistentLogger('put_file'); | ||
/** | ||
@@ -112,2 +115,3 @@ * PUT a file | ||
if (getHeaderNthNode(req.headers) === 3) { | ||
logger.warn({ msg : 'Depth reached', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
@@ -122,2 +126,3 @@ } | ||
let proxy = proxyFactory(() => { | ||
logger.warn({ msg : 'Cannot proxy', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
@@ -136,2 +141,3 @@ }); | ||
}, () => { | ||
logger.warn({ msg : 'Cannot proxy', from : getHeaderFromNode(req.headers) }, { idKittenLogger : req.log_id }); | ||
respond(res, 500); | ||
@@ -171,2 +177,3 @@ }); | ||
if (err) { | ||
logger.warn({ msg : 'Cannot put file', err }, { idKittenLogger : req.log_id }); | ||
return respond(res, 500); | ||
@@ -217,2 +224,3 @@ } | ||
busboy.on('error' , err => { | ||
logger.warn({ msg : 'Cannot put file', err }, { idKittenLogger : req.log_id }); | ||
respond(res, 500); | ||
@@ -219,0 +227,0 @@ }); |
const path = require('path'); | ||
const fs = require('fs'); | ||
const cluster = require('cluster'); | ||
const url = require('url'); | ||
const jwt = require('jsonwebtoken'); | ||
@@ -12,3 +13,5 @@ const cache = require('kitten-cache'); | ||
const PUBLIC_KEY_FILE_EXTENSION = '.pub'; | ||
const ACCESS_KEY_FILE_EXTENSION = '.access_key'; | ||
const keys = {}; | ||
const accessKeys = {}; | ||
@@ -111,2 +114,34 @@ if (cluster.isMaster) { | ||
/** | ||
* Verify accessKey | ||
* @param {Object} req | ||
* @param {Object} res | ||
* @param {Object} params request parameters | ||
* @param {Function} callback | ||
*/ | ||
verifyAccessKey (req, res, params, callback) { | ||
let query = url.parse(req.url).search; | ||
if (!query) { | ||
return respond(res, 401); | ||
} | ||
let queryParams = new URLSearchParams(query); | ||
if (!queryParams.has('access_key')) { | ||
return respond(res, 401); | ||
} | ||
let containerAccessKey = queryParams.get('access_key'); | ||
if (!accessKeys[params.containerId]) { | ||
return respond(res, 401); | ||
} | ||
if (accessKeys[params.containerId] !== containerAccessKey) { | ||
return respond(res, 401); | ||
} | ||
callback(queryParams); | ||
}, | ||
/** | ||
* Load ECDH keys | ||
@@ -126,3 +161,3 @@ * @param {String} directory path to ECDH keys directory | ||
let _filename = path.basename(file, PUBLIC_KEY_FILE_EXTENSION); | ||
let _filename = path.basename(file, PUBLIC_KEY_FILE_EXTENSION); | ||
fs.readFile(path.join(directory, file), (err, file) => { | ||
@@ -138,4 +173,32 @@ if (err) { | ||
}); | ||
} | ||
}, | ||
/** | ||
* Load Access keys | ||
* @param {String} directory path to access keys directory (sameas keys) | ||
*/ | ||
loadAccessKeys : function (directory, callback) { | ||
fs.readdir(directory, (err, files) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
queue(files, (file, next) => { | ||
if (path.extname(file) !== ACCESS_KEY_FILE_EXTENSION) { | ||
return next(); | ||
} | ||
let _filename = path.basename(file, ACCESS_KEY_FILE_EXTENSION); | ||
fs.readFile(path.join(directory, file), (err, file) => { | ||
if (err) { | ||
return next(); | ||
} | ||
accessKeys[_filename] = file.toString().trim(); | ||
next(); | ||
}); | ||
}, callback); | ||
}); | ||
}, | ||
} |
@@ -79,8 +79,3 @@ const http = require('http'); | ||
server = http.createServer((req, res) => { | ||
logger(req, res); | ||
router.lookup(req, res); | ||
}); | ||
server.listen(CONFIG.SERVER_PORT, err => { | ||
auth.loadAccessKeys(path.join(process.cwd(), CONFIG.KEYS_DIRECTORY), err => { | ||
if (err) { | ||
@@ -90,3 +85,14 @@ throw err; | ||
console.log('Server listening on: http://localhost:' + CONFIG.SERVER_PORT); | ||
server = http.createServer((req, res) => { | ||
logger(req, res); | ||
router.lookup(req, res); | ||
}); | ||
server.listen(CONFIG.SERVER_PORT, err => { | ||
if (err) { | ||
throw err; | ||
} | ||
console.log('Server listening on: http://localhost:' + CONFIG.SERVER_PORT); | ||
}); | ||
}); | ||
@@ -93,0 +99,0 @@ }); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 5 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
215
10
6
50416
24
1300