Merge branch 'master' into master
diff --git a/accesscontrol/index.js b/accesscontrol/index.js
new file mode 100644
index 0000000..b9e0cfb
--- /dev/null
+++ b/accesscontrol/index.js
@@ -0,0 +1,135 @@
+'use strict';
+/**
+ * This plugin whitelists or blacklists source ip addresses
+ */
+
+var debug = require('debug')('plugin:accesscontrol');
+var util = require("util");
+const dns = require('dns');
+
+module.exports.init = function (config, logger, stats) {
+
+	var allow;
+	var deny;
+
+	/**
+	* This method reads allow and/or deby lists from the config.yaml
+	* applies the appropriate rule on the incoming message
+	*/
+	function checkAccessControlInfo(sourceIP) {
+		if (config === null) debug('WARNING: insufficient information to run accesscontrol');
+		else if (config.allow === null && config.deny === null) debug('WARNING: insufficient information to run accesscontrol');		
+		else if (config.allow != null) {
+			debug ('allow list: ' + util.inspect(config.allow, 2, true));
+			if (scanIP(config.allow, sourceIP)) {
+				allow = true;
+			}			
+		}
+		else if (config.deny != null) {
+			debug ('deny list: ' + util.inspect(config.deny, 2, true));
+			if (scanIP(config.deny, sourceIP)) {
+				debug ('deny incoming message');
+				deny = true;
+			}			
+		}
+	}
+
+	/**
+	* check if the parameter is valid IPv4 address
+	*/ 
+	function checkIsIPV4(entry) {
+	  var blocks = entry.split(".");
+	  if(blocks.length === 4) {
+	    return blocks.every(function(block) {
+	      return (block === '*' || (parseInt(block,10) >=0 && parseInt(block,10) <= 255));
+	    });
+	  }
+	  return false;
+	}
+
+	/** 
+	* for each list in the allow and deny, make sure they are proper
+	* IPv4 addresses
+	*/
+	function validateIPList(list) {
+		list.forEach(function(entry){
+			if (!checkIsIPV4(entry)) return false;
+		});
+		return true;
+	}
+
+	function scanIP(list, sourceIP) {
+
+		var sourceOctets = sourceIP.split('.');	
+		//no wildcard
+		for (var i=0; i < list.length; i++) {
+			//no wildcard
+			if (list[i].indexOf('*') == -1 && list[i] == sourceIP) {
+				return true;
+			} else if (list[i].indexOf('*') != -1) { //contains wildcard
+				var listOctets = list[i].split('.');
+				if (octetCompare(listOctets, sourceOctets)) return true;			
+			}
+		}
+		//did not match any in the list
+		return false;
+	}
+
+	/**
+	* the allow or deny list contains a wildcard. perform octet level
+	* comparision
+	*/
+	function octetCompare (listOctets, sourceOctets) {
+		var compare = false;
+		for (var i=0; i < listOctets.length; i++) {
+			//debug('list ' + listOctets[i] + ' sourceOctets ' + sourceOctets[i]);
+			if (listOctets[i] != '*' && parseInt(listOctets[i]) == parseInt(sourceOctets[i])) {
+				compare = true;
+			} else if (listOctets[i] != '*' && parseInt(listOctets[i]) != parseInt(sourceOctets[i])) {
+				return false;
+			} 
+		}
+		return compare;
+	}
+
+	/** 
+	* send error message to the user
+	*/
+	function sendError(res) {
+		var errorInfo = {
+			"code": "403",
+			"message": "Forbidden"
+		};
+		res.writeHead(403, { 'Content-Type': 'application/json' });
+		res.write(JSON.stringify(errorInfo));
+		res.end();
+	}
+
+	return {
+
+		onrequest: function(req, res, next) {
+			debug('plugin onrequest');
+			var host = req.headers.host;
+			debug ("source ip " + host);
+			var sourceIP = host.split(":");
+
+			if (checkIsIPV4(sourceIP[0])) {
+				checkAccessControlInfo(sourceIP[0]);
+				if (allow === false || deny === true)
+					sendError(res);
+			} else {
+				dns.lookup(sourceIP[0], (err, address, family) => {
+				  debug('address: %j family: IPv%s', address, family);
+				  if (err) {
+				  	debug(err);
+				  	sendError(res);
+				  }
+				  checkAccessControlInfo(address);
+				  if (allow === false || deny === true)
+				  	sendError(res);				  
+				});
+			}
+			next();
+		}		
+	};
+}
\ No newline at end of file
diff --git a/accesscontrol/package.json b/accesscontrol/package.json
new file mode 100644
index 0000000..696da2b
--- /dev/null
+++ b/accesscontrol/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "edgemicro-access-control",
+  "version": "1.0.0",
+  "description": "This provides ip based whitelisting or blacklisting",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "srinandan",
+  "license": "Apache 2.0",
+  "dependencies": {
+    "debug": "^2.2.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apigee/microgateway-plugins.git"
+  },
+  "bugs": {
+    "url": "https://github.com/apigee/microgateway-plugins/issues"
+  },
+  "homepage": "https://github.com/apigee/microgateway-plugins#readme"
+}
diff --git a/accesscontrol/test/accesscontrol-test.js b/accesscontrol/test/accesscontrol-test.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/accesscontrol/test/accesscontrol-test.js
diff --git a/eurekaclient/eureka-client.json b/eurekaclient/eureka-client.json
new file mode 100644
index 0000000..9f18ea1
--- /dev/null
+++ b/eurekaclient/eureka-client.json
@@ -0,0 +1,15 @@
+{
+	"instance":{
+		"app": "microgateway",
+		"vipAddress": "microgateway",
+		"dataCenterInfo":{
+		  "name": "MyOwn"
+		},
+		"port": {}
+	},
+	"eureka":{
+		"host": "localhost",
+		"port": 8761,
+		"servicePath": "/eureka/apps/"
+	}	
+}
\ No newline at end of file
diff --git a/eurekaclient/index.js b/eurekaclient/index.js
new file mode 100644
index 0000000..b582390
--- /dev/null
+++ b/eurekaclient/index.js
@@ -0,0 +1,95 @@
+'use strict';
+/**
+ * This plugin integrate with Eureka and gets the target
+ * endpoint dynamically. 
+ */
+
+var debug = require('debug')('plugin:eurekeclient');
+var util = require('util');
+var os = require('os');
+
+const port = 8000;
+const Eureka = require('eureka-js-client').Eureka;
+
+module.exports.init = function (config, logger, stats) {
+
+  const lookup = config.servicemap;
+  
+  config.instance.hostName = os.hostname();
+  debug('local hostName: ' + config.instance.hostName);
+  config.instance.ipAddr = getIPAddr();
+  config.instance.port = {};
+  config.instance.port["$"] = port;
+  config.instance.port["@enabled"] = true;
+  config.instance.dataCenterInfo["@class"] = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo";
+
+  const client = new Eureka(config);
+
+  try {
+    client.start();  
+  } catch (err) {
+    console.error(err);
+    client.stop();
+  }
+
+  function getIPAddr() {
+    var interfaces = os.networkInterfaces();
+    var addresses = [];
+    for (var k in interfaces) {
+        for (var k2 in interfaces[k]) {
+            var address = interfaces[k][k2];
+            if (address.family === 'IPv4' && !address.internal) {
+                addresses.push(address.address);
+            }
+        }
+    }
+    debug ('localhost ip: ' + addresses[0]);
+    return addresses[0];  
+  }
+
+  function getAppName(url) {
+    for (var index in lookup) {
+      if (url.includes(lookup[index].uri) || url == lookup[index].uri) {
+        return {
+                  app: lookup[index].app,
+                  secure: lookup[index].secure
+               };
+      }
+    }
+    return "";
+  }
+
+  function getTarget(app, secure) {
+    var instances = client.getInstancesByAppId(app);
+
+    for (var index in instances) {
+      if (instances[index].status == "UP") {
+        return (secure == true) ? {"hostName": instances[index].hostName, "port": instances[index].securePort["$"]} : {"hostName": instances[index].hostName, "port":instances[index].port["$"]};
+      }
+    }
+    return "";
+  }
+
+  return {
+    onrequest: function(req, res, next) {
+      var appInfo = getAppName(req.url);
+      var endpoint = getTarget(appInfo.app, appInfo.secure);
+
+      if (endpoint.hostName) {
+        debug("target hostname: " + endpoint.hostName);
+        req.targetHostname = endpoint.hostName;
+        debug("target port: " + endpoint.port);
+        req.targetPort = endpoint.port;
+        req.targetPath = req.url;
+        if (appInfo.secure) {
+          req.targetSecure = true;
+        } else {
+          req.targetSecure = false;
+        }        
+      } else {
+        console.warn("Target enpoint from Eureka not found");
+      }
+      next();
+    }   
+  };
+}
diff --git a/eurekaclient/package.json b/eurekaclient/package.json
new file mode 100644
index 0000000..c3443bd
--- /dev/null
+++ b/eurekaclient/package.json
@@ -0,0 +1,14 @@
+{
+  "name": "eurekaclient",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "eureka-js-client": "^4.3.0"
+  }
+}
diff --git a/eurekaclient/servicelookup.json b/eurekaclient/servicelookup.json
new file mode 100644
index 0000000..958dfe4
--- /dev/null
+++ b/eurekaclient/servicelookup.json
@@ -0,0 +1 @@
+[{"uri": "/book", "app": "BOOK", "secure": false},{"uri":"/httpbin", "app": "BOOK", "secure": false}]
\ No newline at end of file
diff --git a/index.js b/index.js
index 9fc1317..fd4f20a 100644
--- a/index.js
+++ b/index.js
@@ -10,6 +10,8 @@
   'quota-memory':require('./quota-memory'),
   spikearrest:require('./spikearrest'),
   'transform-uppercase':require('./transform-uppercase'),
+  accesscontrol:require('./accesscontrol'),
+  eurekaclient:require('./eurekaclient')
   json2xml: require('./json2xml'),
   healthcheck: require('./healthcheck')
 }
diff --git a/oauth/index.js b/oauth/index.js
index 75d4ed0..9521cee 100644
--- a/oauth/index.js
+++ b/oauth/index.js
@@ -22,8 +22,32 @@
     var authHeaderName = config['authorization-header'] ? config['authorization-header'] : 'authorization';
     var apiKeyHeaderName = config['api-key-header'] ? config['api-key-header'] : 'x-api-key';
     var keepAuthHeader = config['keep-authorization-header'] || false;
+    //support for enabling oauth or api key only
+    var oauth_only = config['allowOAuthOnly'] || false;
+    var apikey_only = config['allowAPIKeyOnly'] || false;
+    //
     var apiKey;
 
+    //support for enabling oauth or api key only
+    if (oauth_only) {
+      if (!req.headers['authorization']) {
+        debug('missing_authorization');
+        return sendError(req, res, next, logger, stats, 'missing_authorization', 'Missing Authorization header');
+      } else {
+        var header = authHeaderRegex.exec(req.headers['authorization']);
+        if (!header || header.length < 2) {
+          debug('Invalid Authorization Header');
+          return sendError(req, res, next, logger, stats, 'invalid_request', 'Invalid Authorization header');
+        }
+      }
+    }
+    else if (apikey_only) {
+      if (req.headers[authHeaderName]) {
+        debug('Invalid Authorization Type');
+        return sendError(req, res, next, logger, stats, 'invalid_auth', 'Invalid Authorization type');        
+      }
+    }
+    //leaving rest of the code same to ensure backward compatibility
     if (!req.headers[authHeaderName]) {
       if (apiKey = req.headers[apiKeyHeaderName]) {
         exchangeApiKeyForToken(req, res, next, config, logger, stats, middleware, apiKey);