Skip to content
This repository has been archived by the owner on Apr 21, 2020. It is now read-only.

Commit

Permalink
Begin adding support for oauth proxying in addition to reverse proxying.
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanbreen committed Mar 15, 2015
1 parent 24ab397 commit 5354a15
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 2 deletions.
14 changes: 14 additions & 0 deletions lib/proxy/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ var _ = require('underscore');
function ProxyConfig(config) {
var this_obj = this;

// oauth_[reverse_|]proxy supports two modes of operation: a proxy for signing outbound requests on behalf
// of clients and a reverse proxy for authenticating requests before they reach a server. The default is
// reverse proxy.
var mode = 'reverse_proxy';
if (config.mode === 'proxy') mode = config.mode;
Object.defineProperty(this_obj, 'mode', { 'value': mode, writable: false, enumerable: true });

// We want all of these parameters to be immutable. While writeable defaults to false, having it explicitly
// set here is good for readability.
Object.defineProperty(this_obj, 'service_name', { 'value': config.service_name, writable: false, enumerable: true });
Expand Down Expand Up @@ -55,6 +62,13 @@ ProxyConfig.prototype.equals = function(other_proxy_config) {
return _.isEqual(this, other_proxy_config);
};

/**
* Returns true if we are in 'reverse_proxy' mode, false else.
*/
ProxyConfig.prototype.isReverseProxy = function() {
return this.mode === 'reverse_proxy';
};

/**
* Returns a string matching the first validation that failed when evaluating this proxy config or returns
* undefined if the configuration is valid.
Expand Down
79 changes: 77 additions & 2 deletions lib/proxy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,82 @@ function Proxy(proxy_config) {
}

Proxy.prototype.getConnectApp = function() {
return this.config.isReverseProxy() ? getConnectReverseProxyApp() : getConnectProxyApp();
};

/**
* Returns a connect pipeline for evaluating inbound requests and applying OAuth signatures before
* forwarding them on.
*/
Proxy.prototype.getConnectProxyApp = function() {

var this_obj = this;

// Validators
var quota_validator = require('./validators/quota_validator.js')(this_obj);
var request_sanity_validator = require('./validators/request_sanity_validator.js')(this_obj);
var url_length_validator = require('./validators/url_length_validator.js')(this_obj);

// Mutators
var form_parser = connect.urlencoded();
var forward_header_mutator = require('./mutators/forward_header_mutator.js')(this_obj);
var host_header_mutator = require('./mutators/host_header_mutator.js')(this_obj);
var oauth_signer = require('./mutators/oauth_signer.js')(this_obj);
var query_string_parser = connect.query();
var restreamer = require('./mutators/restreamer.js')({stringify:require('querystring').stringify});
var url_parser = require('./mutators/url_parser.js')(this_obj);

var proxy = httpProxy.createProxyServer({});

// Handle connection errors to the remote service. Normal errors returned by the
// service (like 404s) will get proxied through without any tampering.
proxy.on('error', function(err, req, res) {
this_obj.logger.info(module_tag, "Got error %s communicating with remote server.", util.inspect(err));
res.writeHead(500, "Connection to " + this_obj.config.service_name + " failed");
res.end();
});

// Return an all-singing, all-dancing OAuth signing connect pipeline.
return connect(
// Test for minimum viable sanity for an inbound request. Pass in the proxy object so that
// the URI and Host header can be matched against the expected values, if provided.
request_sanity_validator,
// Reject request with URLs longer than 16kb
url_length_validator,
// Unpack the body of POSTs so we can use them in signatures. Note that this
// will implicitly limit POST size to 1mb. We may wish to add configuration around
// this in the future.
form_parser,
// Parse query string
query_string_parser,
// Parse url once so that it's available in a clean format for the oauth validator
url_parser,
// Modify the request headers to add x-forwarded-*
forward_header_mutator,
// Check the request against our path/verb whitelist
whitelist_validator,
// Validate that the request is within quota
quota_validator,
// Validate that the timestamp of the request is legal
oauth_signer,
// Update the host header
host_header_mutator,
// Since connect messes with the input parameters and we want to pass them through
// unadulterated to the target, we need to add restreamer to the chain.
restreamer,
// Whew. After all of that, we're ready to proxy the request.
function(req, res) {
// Proxy a web request to the target port on localhost using the provided agent.
// If no agent is provided, node-http-proxy will return a connection: close.
proxy.web(req, res, {agent: HTTP_AGENT, target: { 'host' : 'localhost', 'port' : this_obj.config.to_port }});
}
);
};

/**
* Returns a connect pipeline for evaluating inbound requests and validating their OAuth signatures.
*/
Proxy.prototype.getConnectReverseProxyApp = function() {

var this_obj = this;

Expand Down Expand Up @@ -111,8 +187,7 @@ Proxy.prototype.getConnectApp = function() {
// Update the host header
host_header_mutator,
// Since connect messes with the input parameters and we want to pass them through
// unadulterated to the target, we need to add restreamer to the chain. But we only
// need to do this if we're given a formencoded request.
// unadulterated to the target, we need to add restreamer to the chain.
restreamer,
// Whew. After all of that, we're ready to proxy the request.
function(req, res) {
Expand Down

0 comments on commit 5354a15

Please sign in to comment.