oauth_reverse_proxy
Advanced tools
Comparing version 0.9.5 to 0.9.6
@@ -1,14 +0,42 @@ | ||
module.exports = function(grunt) { | ||
grunt.initConfig({ | ||
coveralls: { | ||
options: { | ||
src: 'reports/lcov.info', | ||
} | ||
} | ||
}); | ||
grunt.loadNpmTasks('grunt-coveralls'); | ||
// Default task(s). | ||
grunt.registerTask('default', ['coveralls']); | ||
} | ||
module.exports = function(grunt){ | ||
grunt.initConfig({ | ||
env: { | ||
common: { | ||
}, | ||
unix: { | ||
OAUTH_REVERSE_PROXY_HOME: '/etc/oauth_reverse_proxy.d' | ||
}, | ||
win: { | ||
OAUTH_REVERSE_PROXY_HOME: 'c:\\ProgramData\\oauth_reverse_proxy.d' | ||
} | ||
}, | ||
benchmark: { | ||
all: { | ||
src: ['test/benchmarks/*.js'], | ||
dest: 'reports/benchmark-results.csv' | ||
} | ||
}, | ||
nodemon: { | ||
dev: { | ||
script: 'index.js' | ||
} | ||
} | ||
}); | ||
grunt.loadNpmTasks('grunt-benchmark'); | ||
grunt.loadNpmTasks('grunt-env'); | ||
grunt.loadNpmTasks('grunt-nodemon'); | ||
grunt.registerTask('default', function() { | ||
grunt.task.run('env:common'); | ||
if(/^win/.test(process.platform)) { | ||
grunt.task.run('env:win'); | ||
grunt.task.run('nodemon'); | ||
} | ||
else { | ||
grunt.task.run('env:unix'); | ||
grunt.task.run('nodemon'); | ||
} | ||
}); | ||
}; |
@@ -43,2 +43,3 @@ var fs = require('fs'); | ||
fs.readdir(config_dir, function(err, files) { | ||
/* istanbul ignore if */ | ||
if (err) return cb(err); | ||
@@ -74,3 +75,4 @@ | ||
} catch(e) { | ||
proxies[file] = "Failed to parse configuration for proxy:\n" + data; | ||
logger.error("Failed to load proxy %s due to %s", file, e); | ||
proxies[file] = e.message; | ||
return wrapped_cb(); | ||
@@ -83,2 +85,3 @@ } | ||
proxy.start(function(err) { | ||
/* istanbul ignore if */ | ||
if (err) { | ||
@@ -98,5 +101,6 @@ // CONTROVERSIAL STATEMENT ALERT: we do not consider a startup error with a | ||
}); | ||
/* istanbul ignore next */ | ||
} catch(e) { | ||
/* istanbul ignore next */ | ||
proxies[file] = "Uncaught exception starting proxy " + proxy_config.service_name + ": " + e + "\n" + e.stack; | ||
/* istanbul ignore next */ | ||
wrapped_cb(); | ||
@@ -103,0 +107,0 @@ } |
@@ -9,2 +9,4 @@ var util = require('util'); | ||
var Whitelist = require('./whitelist.js'); | ||
// Only allow requests within 5 minutes. | ||
@@ -73,3 +75,3 @@ var MAX_AGE = 5*60*1000; | ||
function safelyAddValues(argument_pairs, name, value) { | ||
value = value || ""; | ||
value = value || /* istanbul ignore next */ ""; | ||
if (Array.isArray(value)) { | ||
@@ -298,2 +300,20 @@ for (var i=0; i<value.length; ++i) { | ||
/** | ||
* Create a whitelist validator. If the request passes the whitelist, it is automatically proxied through. | ||
*/ | ||
exports.whitelistValidator = function(config){ | ||
var whitelist = new Whitelist(config); | ||
return function(req,res,next) { | ||
req.whitelist_passed = whitelist.applyWhitelist(req); | ||
if(req.whitelist_passed) { | ||
logger.info("Proxying URL %s %s%s WHITELIST", req.method, req.headers.host, req.url); | ||
} | ||
return next(); | ||
}; | ||
}; | ||
// Run a series of validations on the request. If any of the validations fail, an error response will be | ||
@@ -300,0 +320,0 @@ // written to the client, and this method will terminate with error. |
@@ -28,4 +28,4 @@ var fs = require('fs'); | ||
// Use a default whitelist config if none is provided. | ||
var whitelist = config.whitelist || { | ||
paths: [{ | ||
var whitelist = config.whitelist || | ||
[{ | ||
path: "/livecheck", | ||
@@ -35,6 +35,5 @@ methods: [ "GET" ] | ||
{ | ||
path: "healthcheck", | ||
path: "/healthcheck", | ||
methods: [ "GET" ] | ||
}] | ||
}; | ||
}]; | ||
@@ -41,0 +40,0 @@ Object.defineProperty(this_obj, 'whitelist', { 'value': whitelist, writable: false }); |
@@ -66,3 +66,3 @@ var _ = require('underscore'); | ||
var apply_whitelist = whitelist.applyWhitelist(this_obj.config); | ||
var whitelist_validator = authenticator.whitelistValidator(this_obj.config); | ||
var oauth_validator = authenticator.oauthValidator(this_obj.keys); | ||
@@ -97,3 +97,3 @@ var modify_host_header = header_modifier.modifyHostHeaders(this_obj.config.from_port, this_obj.config.to_port); | ||
// Check the request against our path/verb whitelist | ||
apply_whitelist, | ||
whitelist_validator, | ||
// Add our oauth validator in front of the proxy | ||
@@ -100,0 +100,0 @@ oauth_validator, |
@@ -7,83 +7,67 @@ var _ = require('underscore'); | ||
function Whitelist (config) { | ||
Object.defineProperty(this, 'whitelist', {value: config.whitelist}); | ||
} | ||
/** | ||
* Each whitelist entry can take the form: | ||
* { | ||
* "path": "/path/string/or/regex", | ||
* "method": [ "GET", "POST" ] | ||
* } | ||
* | ||
* A predicate is considered matched if both conditions (that is, path and method) | ||
* match an inbound request. Paths are considered to be regexes and must match the | ||
* entire path of the request. | ||
* | ||
* Either path or method may be omitted. Note that path is always a string and method | ||
* is always an array. | ||
*/ | ||
function createWhitelistPredicate(config) { | ||
if (config.path) { | ||
var path_regex = new RegExp("^" + config.path + "$", "i"); | ||
} | ||
// See if our requested path is found in the whitelist | ||
Whitelist.prototype._findMatchingPath = function (path) { | ||
return _.find(this.whitelist.paths, function(path_object) { | ||
var path_regex = new RegExp("^" + path_object.path + "$", "i"); | ||
return (path.match(path_regex) !== null); | ||
if (config.methods) { | ||
var methods = {}; | ||
_.each(config.methods, function(method) { | ||
methods[method] = true; | ||
}); | ||
} | ||
} | ||
// Check if our requested method has been allowed for this path | ||
Whitelist.prototype._passesMethodsForPath = function (path, method) { | ||
// If the request's path is in the whitelist, ensure our method is. | ||
if ("methods" in path) { | ||
if(Array.isArray(path.methods) && path.methods.indexOf(method) !== -1) { | ||
return true | ||
} | ||
else if(typeof path.methods === "string" && path.methods === method) { | ||
return true | ||
} | ||
} | ||
else { | ||
return true | ||
} | ||
return false; | ||
} | ||
// A whitelist predicate with no valid config should be ignored. | ||
if (path_regex === undefined && methods === undefined) return undefined; | ||
// Check if the requested method has been blanket whitelisted | ||
Whitelist.prototype._passesMethodsWhitelist = function (method) { | ||
if ("methods" in this.whitelist) { | ||
if (typeof this.whitelist.methods === "string" && this.whitelist.methods === method) { | ||
return true | ||
} | ||
else if (Array.isArray(this.whitelist.methods) && this.whitelist.methods.indexOf(method) !== -1) { | ||
return true | ||
} | ||
} | ||
return false | ||
return function(req) { | ||
// A predicate is considered to match if it matches both the path and methods | ||
// configuration of the predicate. If either is ommitted, it's assumed to be | ||
// a match. | ||
var matches_method = (methods != undefined) ? methods[req.method] != undefined : true; | ||
var matches_path = (path_regex != undefined) ? path_regex.test(req.parsed_url.pathname) : true; | ||
return matches_method && matches_path; | ||
}; | ||
} | ||
// Check if the requested path AND method has been whitelisted | ||
Whitelist.prototype._passesPathsWhitelist = function (path, method) { | ||
if ("paths" in this.whitelist) { | ||
if(Array.isArray(this.whitelist.paths)) { | ||
var current_path = this._findMatchingPath(path); | ||
if (current_path !== undefined) { | ||
return this._passesMethodsForPath(current_path, method); | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* A whitelist is a collection of predicates run on any inbound req. If any of the predicates | ||
* returns true when passed the req, the req is said to be whitelisted. | ||
*/ | ||
function Whitelist(config) { | ||
var whitelist_config = config.whitelist; | ||
var predicates = []; | ||
Object.defineProperty(this, 'predicates', {value: predicates}); | ||
Whitelist.prototype.applyWhitelist = function(req){ | ||
return (this._passesMethodsWhitelist(req.method) | ||
|| this._passesPathsWhitelist(req.parsed_url.pathname, req.method)); | ||
whitelist_config.forEach(function(predicate_config) { | ||
var p = createWhitelistPredicate(predicate_config); | ||
if (p) predicates.push(p); | ||
else logger.error("Invalid predicate config %s", util.inspect(predicate_config)); | ||
}); | ||
} | ||
/** | ||
* Use the whitelist to decide if we should let the request through unauthenticated | ||
* Pass the request through all predicates. | ||
*/ | ||
exports.applyWhitelist = function(config) { | ||
var whitelist = new Whitelist(config); | ||
return function(req,res,next) { | ||
req.whitelist_passed = whitelist.applyWhitelist(req); | ||
if(req.whitelist_passed) { | ||
logger.info("Proxying URL %s %s%s WHITELIST", req.method, req.headers.host, req.url); | ||
} | ||
return next(); | ||
}; | ||
Whitelist.prototype.applyWhitelist = function(req) { | ||
for (var i=0; i<this.predicates.length; ++i) { | ||
if (this.predicates[i](req)) return true; | ||
} | ||
return false; | ||
}; | ||
exports.Whitelist = Whitelist; | ||
module.exports = Whitelist; |
{ | ||
"name": "oauth_reverse_proxy", | ||
"description": "An OAuth 1.0a authenticating reverse proxy", | ||
"version": "0.9.5", | ||
"version": "0.9.6", | ||
"contributors": [ | ||
@@ -27,3 +27,6 @@ { | ||
"grunt": "^0.4.5", | ||
"grunt-benchmark": "^0.3.0", | ||
"grunt-coveralls": "^1.0.0", | ||
"grunt-env": "^0.4.2", | ||
"grunt-nodemon": "^0.3.0", | ||
"istanbul": "0.2.11", | ||
@@ -30,0 +33,0 @@ "method-override": "1.0.2", |
@@ -91,3 +91,3 @@ var should = require('should'); | ||
[ | ||
'unnamed_service.json', | ||
'1999_called_service.xml', 'unnamed_service.json', | ||
'no_from_port_service.json', 'no_to_port_service.json', | ||
@@ -94,0 +94,0 @@ 'equal_ports_service.json', |
@@ -1,65 +0,7 @@ | ||
var Whitelist = require('../../lib/whitelist.js').Whitelist | ||
var fs = require('fs'); | ||
var config_short = { | ||
whitelist: { | ||
methods: ["GET"], | ||
paths: [ | ||
{ | ||
path: "/livecheck" | ||
}, | ||
{ | ||
path: "/resources/item", | ||
methods: "PUT" | ||
}, | ||
{ | ||
path: "/v2/another/route", | ||
methods: ["DELETE","POST"] | ||
}] | ||
} | ||
} | ||
var Whitelist = require('../../lib/proxy/whitelist.js'); | ||
var config_long = { | ||
whitelist: { | ||
paths: [ | ||
{ | ||
path: "/livecheck" | ||
}, | ||
{ | ||
path: "/resources/item", | ||
methods: "PUT" | ||
}, | ||
{ | ||
path: "/v2/another/route", | ||
methods: ["DELETE","POST"] | ||
}, | ||
{ | ||
path: "/things/details", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/etails", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/tails", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/ails", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/ils", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/ls", | ||
methods: ["PUT"] | ||
}, | ||
{ | ||
path: "/things/[\\d]/s", | ||
methods: ["PUT"] | ||
}] | ||
} | ||
} | ||
var config_short = JSON.parse(fs.readFileSync('./test/resources/short_whitelist.json', {'encoding':'utf8'})); | ||
var config_long = JSON.parse(fs.readFileSync('./test/resources/long_whitelist.json', {'encoding':'utf8'})); | ||
@@ -66,0 +8,0 @@ var req_simple = { method: "GET", |
@@ -5,2 +5,4 @@ var should = require('should'); | ||
var Whitelist = require('../lib/proxy/whitelist.js'); | ||
// All tests must require auth_proxy_bootstrap_test since that creates our proxy, starts our job server, and | ||
@@ -31,3 +33,3 @@ // and registers a beforeEach to keep the request_sender and job_server clean between test runs. | ||
['GET', 'POST', 'PUT', 'DELETE'].forEach(function(verb) { | ||
if (verb === 'GET' && url.toLowerCase() === 'http://localhost:8008/livecheck' ) | ||
if (verb === 'GET' && (url.toLowerCase() === 'http://localhost:8008/livecheck' || url.toLowerCase() === 'http://localhost:8008/healthcheck') ) | ||
it ("should allow GET " + url + " through without authentication", create_livecheck_test(verb, url, 200)); | ||
@@ -38,2 +40,34 @@ else | ||
}); | ||
var config_short = JSON.parse(fs.readFileSync('./test/resources/short_whitelist.json', {'encoding':'utf8'})); | ||
var config_long = JSON.parse(fs.readFileSync('./test/resources/long_whitelist.json', {'encoding':'utf8'})); | ||
it ("should support blanket method matching", function() { | ||
var whitelist_short = new Whitelist(config_short); | ||
whitelist_short.applyWhitelist({ | ||
method: "GET", | ||
parsed_url: { | ||
pathname: "/livecheck" | ||
} | ||
}).should.equal(true); | ||
}); | ||
it ("should support path matching", function() { | ||
var whitelist_short = new Whitelist(config_short); | ||
whitelist_short.applyWhitelist({ | ||
method: "POST", | ||
parsed_url: { | ||
pathname: "/livecheck" | ||
} | ||
}).should.equal(true); | ||
var whitelist_long = new Whitelist(config_long); | ||
whitelist_long.applyWhitelist({ | ||
method: "PUT", | ||
parsed_url: { | ||
pathname: "/things/123/s" | ||
} | ||
}).should.equal(true); | ||
}); | ||
}); |
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
15825453
88
3169
19
25