From cd99c1b5e57bd50bbbd2b17563e450eee2bddc9d Mon Sep 17 00:00:00 2001 From: Rowan Hill Date: Mon, 2 Dec 2013 23:33:51 +0000 Subject: [PATCH 1/2] Extensible request signing --- README.md | 7 ++++--- app.js | 37 ++++++++++++++++++----------------- signers/common/crypto_hash.js | 10 ++++++++++ signers/signed_md5.js | 7 +++++++ signers/signed_sha256.js | 7 +++++++ 5 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 signers/common/crypto_hash.js create mode 100644 signers/signed_md5.js create mode 100644 signers/signed_sha256.js diff --git a/README.md b/README.md index 31ff7561..5dbbd0fd 100644 --- a/README.md +++ b/README.md @@ -288,11 +288,12 @@ Line: (5) is set to "key" 8. "signature" is a JSON object that contains the details about - the API call signing requirements. The signature routine coded - in app.js is a hash of the string concatenation of API key, + the API call signing requirements. The bundled signature + routines create a hash of the string concatenation of API key, API key secret and timestamp (epoch). -9. "type" key value is either *signed_md5* or *signed_sha256*. +9. "type" key value is a module in the `signers` directory. By + default this is either *signed_md5* or *signed_sha256*. More signature methods are available with crypto.js, but have not been included in the code as options. diff --git a/app.js b/app.js index c8f06403..56d41416 100755 --- a/app.js +++ b/app.js @@ -34,7 +34,6 @@ var express = require('express'), url = require('url'), http = require('http'), https = require('https'), - crypto = require('crypto'), redis = require('redis'), RedisStore = require('connect-redis')(express); @@ -783,23 +782,6 @@ function processRequest(req, res, next) { options.path += apiConfig.keyParam + '=' + apiKey; } - // Perform signature routine, if any. - if (apiConfig.signature) { - var timeStamp, sig; - if (apiConfig.signature.type == 'signed_md5') { - // Add signature parameter - timeStamp = Math.round(new Date().getTime()/1000); - sig = crypto.createHash('md5').update('' + apiKey + apiSecret + timeStamp + '').digest(apiConfig.signature.digest); - options.path += '&' + apiConfig.signature.sigParam + '=' + sig; - } - else if (apiConfig.signature.type == 'signed_sha256') { // sha256(key+secret+epoch) - // Add signature parameter - timeStamp = Math.round(new Date().getTime()/1000); - sig = crypto.createHash('sha256').update('' + apiKey + apiSecret + timeStamp + '').digest(apiConfig.signature.digest); - options.path += '&' + apiConfig.signature.sigParam + '=' + sig; - } - } - // Setup headers, if any if (reqQuery.headerNames && reqQuery.headerNames.length > 0) { if (config.debug) { @@ -848,6 +830,25 @@ function processRequest(req, res, next) { doRequest = http.request; } + // Perform signature routine, if any. + if (apiConfig.signature) { + var signerModuleName = null; + if (fs.existsSync(path.join('./signers', apiConfig.signature.type + '.js'))) { + signerModuleName = './signers/' + apiConfig.signature.type + '.js'; + } + + if (signerModuleName != null) { + var signer = require(signerModuleName); + if (signer.signRequest) { + signer.signRequest(httpMethod, url, requestBody, options, apiKey, apiSecret, apiConfig.signature); + } else { + console.error('Signer "' + apiConfig.signature.type + '" does not have a signRequest() method'); + } + } else { + console.error('Could not find signer "' + apiConfig.signature.type + '"'); + } + } + // API Call. response is the response from the API, res is the response we will send back to the user. var apiCall = doRequest(options, function(response) { response.setEncoding('utf-8'); diff --git a/signers/common/crypto_hash.js b/signers/common/crypto_hash.js new file mode 100644 index 00000000..eca57675 --- /dev/null +++ b/signers/common/crypto_hash.js @@ -0,0 +1,10 @@ +var crypto = require('crypto'); + +var signRequest = function(hashType, options, apiKey, apiSecret, signatureConfig) { + // Add signature parameter + var timeStamp = Math.round(new Date().getTime()/1000); + var sig = crypto.createHash(hashType).update('' + apiKey + apiSecret + timeStamp + '').digest(signatureConfig.digest); + options.path += '&' + signatureConfig.sigParam + '=' + sig; +}; + +exports.signRequest = signRequest; \ No newline at end of file diff --git a/signers/signed_md5.js b/signers/signed_md5.js new file mode 100644 index 00000000..f6e97a26 --- /dev/null +++ b/signers/signed_md5.js @@ -0,0 +1,7 @@ +var cryptoHash = require('./common/crypto_hash.js'); + +var signRequest = function(httpMethod, url, requestBody, options, apiKey, apiSecret, signatureConfig) { + cryptoHash.signRequest('md5', options, apiKey, apiSecret, signatureConfig); +}; + +exports.signRequest = signRequest; \ No newline at end of file diff --git a/signers/signed_sha256.js b/signers/signed_sha256.js new file mode 100644 index 00000000..63d8a5cf --- /dev/null +++ b/signers/signed_sha256.js @@ -0,0 +1,7 @@ +var cryptoHash = require('./common/crypto_hash.js'); + +var signRequest = function(httpMethod, url, requestBody, options, apiKey, apiSecret, signatureConfig) { + cryptoHash.signRequest('sha256', options, apiKey, apiSecret, signatureConfig); +}; + +exports.signRequest = signRequest; \ No newline at end of file From 6490b9dae09ae60a08ad8382996826ae59ea2a84 Mon Sep 17 00:00:00 2001 From: Rowan Hill Date: Sat, 7 Dec 2013 12:41:31 +0000 Subject: [PATCH 2/2] Allow configuration of custom signers directory --- README.md | 5 +++-- app.js | 12 +++++++++++- config.json.sample | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5dbbd0fd..29de0f87 100644 --- a/README.md +++ b/README.md @@ -292,8 +292,9 @@ Line: routines create a hash of the string concatenation of API key, API key secret and timestamp (epoch). -9. "type" key value is a module in the `signers` directory. By - default this is either *signed_md5* or *signed_sha256*. +9. "type" key value is a module in the `signers` directory, or in + the directory specified by `customSignersDir` in config.json. + By default this is either *signed_md5* or *signed_sha256*. More signature methods are available with crypto.js, but have not been included in the code as options. diff --git a/app.js b/app.js index 56d41416..4ab26b4b 100755 --- a/app.js +++ b/app.js @@ -77,6 +77,14 @@ if (!fs.existsSync(config.apiConfigDir)) { process.exit(1); } +if (config.customSignersDir) { + config.customSignersDir = path.resolve(config.customSignersDir); + if (!fs.existsSync(config.customSignersDir)) { + console.error("Could not find custom request signers directory: " + config.customSignersDir); + process.exit(1); + } +} + try { var apisConfig = require(path.join(config.apiConfigDir, 'apiconfig.json')); if (config.debug) { @@ -833,7 +841,9 @@ function processRequest(req, res, next) { // Perform signature routine, if any. if (apiConfig.signature) { var signerModuleName = null; - if (fs.existsSync(path.join('./signers', apiConfig.signature.type + '.js'))) { + if (fs.existsSync(path.join(config.customSignersDir, apiConfig.signature.type + '.js'))) { + signerModuleName = config.customSignersDir + '/' + apiConfig.signature.type + '.js'; + } else if (fs.existsSync(path.join('./signers', apiConfig.signature.type + '.js'))) { signerModuleName = './signers/' + apiConfig.signature.type + '.js'; } diff --git a/config.json.sample b/config.json.sample index 1983fc97..1e8491a7 100644 --- a/config.json.sample +++ b/config.json.sample @@ -9,6 +9,7 @@ "username" : "", "password" : "" }, + "customSignersDir" : null, "redis" : { "host" : "localhost", "port" : 6379,