apiKey and ClientID in query string (#228)

diff --git a/scripts/lua/policies/security/apiKey.lua b/scripts/lua/policies/security/apiKey.lua
index 1336243..513a3b9 100644
--- a/scripts/lua/policies/security/apiKey.lua
+++ b/scripts/lua/policies/security/apiKey.lua
@@ -73,8 +73,16 @@
   local gatewayPath = ngx.var.gatewayPath
   local apiId = dataStore:resourceToApi(utils.concatStrings({'resources:', tenant, ':', gatewayPath}))
   local scope = securityObj.scope
-  local header = (securityObj.header == nil) and 'x-api-key' or securityObj.header
-  local apiKey = ngx.var[utils.concatStrings({'http_', header}):gsub("-", "_")]
+  local name = (securityObj.name == nil) and 'x-api-key' or securityObj.name
+  local queryString = ngx.req.get_uri_args()
+  local location = (securityObj.location == nil) and 'header' or securityObj.location
+  local apiKey = nil
+  if location == "header" then
+    apiKey = ngx.var[utils.concatStrings({'http_', name}):gsub("-", "_")]
+  end
+  if location == "query" then
+    apiKey = queryString[name]
+  end
   if apiKey == nil or apiKey == '' then
     request.err(401, 'Unauthorized')
     return nil
diff --git a/scripts/lua/policies/security/clientSecret.lua b/scripts/lua/policies/security/clientSecret.lua
index 85224d7..af865af 100644
--- a/scripts/lua/policies/security/clientSecret.lua
+++ b/scripts/lua/policies/security/clientSecret.lua
@@ -50,29 +50,40 @@
   local gatewayPath = ngx.var.gatewayPath
   local apiId = ngx.var.apiId
   local scope = securityObj.scope
+  local queryString = ngx.req.get_uri_args()
+  local location = (securityObj.location == nil) and 'header' or securityObj.location
+  local clientId = nil
+  local clientSecret = nil
 
-  -- allow support for custom headers
-  local location = (securityObj.keyLocation == nil) and 'http_' or securityObj.keyLocation
-  if location == 'header' then
-    location = 'http_'
-  end
-
+  -- allow support for custom names in query or header
   local clientIdName = (securityObj.idFieldName == nil) and 'X-Client-ID' or securityObj.idFieldName
-
-  local clientId = ngx.var[utils.concatStrings({location, clientIdName}):gsub("-", "_")]
-  -- if they didn't supply whatever header this is configured to require, error out
+  if location == "header" then
+    clientId = ngx.var[utils.concatStrings({'http_', clientIdName}):gsub("-", "_")]
+  end
+  if location == "query" then
+    clientId = queryString[clientIdName]
+  end
+-- if they didn't supply whatever name this is configured to require, error out
   if clientId == nil or clientId == '' then
     request.err(401, clientIdName .. " required")
     return false
   end
+
+-- allow support for custom names in query or header
   local clientSecretName = (securityObj.secretFieldName == nil) and 'X-Client-Secret' or securityObj.secretFieldName
   _G.clientSecretName = clientSecretName:lower()
-
-  local clientSecret = ngx.var[utils.concatStrings({location, clientSecretName}):gsub("-","_")]
+  if location == "header" then
+    clientSecret = ngx.var[utils.concatStrings({'http_', clientSecretName}):gsub("-","_")]
+  end
+  if location == "query" then
+    clientSecret = queryString[clientSecretName]
+  end
+-- if they didn't supply whatever name this is configured to require, error out
   if clientSecret == nil or clientSecret == '' then
     request.err(401, clientSecretName .. " required")
     return false
   end
+
 -- hash the secret
   local result = validate(dataStore, tenant, gatewayPath, apiId, scope, clientId, hashFunction(clientSecret))
   if result == nil then
diff --git a/tests/scripts/lua/security.lua b/tests/scripts/lua/security.lua
index 0e04984..239650d 100644
--- a/tests/scripts/lua/security.lua
+++ b/tests/scripts/lua/security.lua
@@ -30,7 +30,7 @@
     local red = fakeredis.new()
     local ngx = fakengx.new()
     local ds = require "lib/dataStore"
-    local dataStore = ds.initWithDriver(red) 
+    local dataStore = ds.initWithDriver(red)
     local ngxattrs = cjson.decode([[
       {
         "tenant":"abcd",
@@ -39,6 +39,7 @@
       }
     ]])
     ngx.var = ngxattrs
+    ngx.req = { get_uri_args = function() return {} end }
     _G.ngx = ngx
     local securityObj = cjson.decode([[
       {
@@ -51,6 +52,34 @@
     local key = apikey.process(dataStore, securityObj, function() return "fakehash" end)
     assert.same(key, 'a1234')
   end)
+  it('Checks an apiKey correctly in a query string', function()
+    local red = fakeredis.new()
+    local ngx = fakengx.new()
+
+    local ds = require "lib/dataStore"
+    local dataStore = ds.initWithDriver(red)
+    local ngxattrs = cjson.decode([[
+      {
+        "tenant":"abcd",
+        "gatewayPath":"v1/test"
+      }
+    ]])
+    ngx.var = ngxattrs
+    ngx.req = { get_uri_args = function() return { apiKey = "a1234" } end }
+    _G.ngx = ngx
+    local securityObj = cjson.decode([[
+      {
+        "scope":"api",
+        "type":"apikey",
+        "name":"apiKey",
+        "location":"query"
+      }
+    ]])
+    red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
+    red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
+    local key = apikey.process(dataStore, securityObj, function() return "fakehash" end)
+    assert.same(key, 'a1234')
+  end)
   it('Returns nil with a bad apikey', function()
     local red = fakeredis.new()
     local ds = require "lib/dataStore"
@@ -64,6 +93,7 @@
       }
     ]])
     ngx.var = ngxattrs
+    ngx.req = { get_uri_args = function() return { apiKey = "a1234" } end }
     _G.ngx = ngx
     local securityObj = cjson.decode([[
       {
@@ -88,12 +118,40 @@
       }
     ]])
     ngx.var = ngxattrs
+    ngx.req = { get_uri_args = function() return {} end}
     _G.ngx = ngx
     local securityObj = cjson.decode([[
       {
         "scope":"api",
         "type":"apikey",
-        "header":"x-test-key"
+        "name":"x-test-key"
+      }
+    ]])
+    red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
+    red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
+    local key = apikey.process(dataStore, securityObj, function() return "fakehash" end)
+    assert.same(key, 'a1234')
+  end)
+  it('Checks for a key with a custom name in the query string', function()
+    local red = fakeredis.new()
+    local ds = require "lib/dataStore"
+    local dataStore = ds.initWithDriver(red)
+    local ngx = fakengx.new()
+    local ngxattrs = cjson.decode([[
+      {
+        "tenant":"abcd",
+        "gatewayPath":"v1/test"
+      }
+    ]])
+    ngx.var = ngxattrs
+    ngx.req = { get_uri_args = function () return { xtestkey = "a1234" } end }
+    _G.ngx = ngx
+    local securityObj = cjson.decode([[
+      {
+        "scope":"api",
+        "type":"apikey",
+        "name":"xtestkey",
+        "location":"query"
       }
     ]])
     red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
@@ -103,8 +161,8 @@
   end)
   it('Checks for a key with a custom header and hash configuration', function()
     local red = fakeredis.new()
-    local ds = require "lib/dataStore" 
-    local dataStore = ds.initWithDriver(red) 
+    local ds = require "lib/dataStore"
+    local dataStore = ds.initWithDriver(red)
     local ngx = fakengx.new()
     local ngxattrs = cjson.decode([[
       {
@@ -113,13 +171,14 @@
         "http_x_test_key":"a1234"
       }
     ]])
+    ngx.req = { get_uri_args = function() return {} end }
     ngx.var = ngxattrs
     _G.ngx = ngx
     local securityObj = cjson.decode([[
       {
         "scope":"api",
         "type":"apikey",
-        "header":"x-test-key",
+        "name":"x-test-key",
         "hashed":true
       }
     ]])
@@ -155,7 +214,7 @@
     assert(result)
   end)
   it('Exchanges a bad token, doesn\'t cache it and returns false', function()
-    local red = fakeredis.new()  
+    local red = fakeredis.new()
     local token = "bad"
     local ngxattrs = [[
       {
@@ -180,7 +239,7 @@
   end)
   it('Loads a facebook token from the cache without a valid app id', function()
     local red = fakeredis.new()
-    local ds = require "lib/dataStore" 
+    local ds = require "lib/dataStore"
     local dataStore = ds.initWithDriver(red)
     local token = "test"
     local ngxattrs = [[
@@ -247,6 +306,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
+    ngx.req = { get_uri_args = function() return {} end }
     ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
@@ -270,6 +330,7 @@
         "gatewayPath":"v1/test"
       }
     ]]
+    ngx.req = { get_uri_args = function() return {} end }
     ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
@@ -294,6 +355,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
+    ngx.req = { get_uri_args = function() return {} end }
     ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[
@@ -313,6 +375,7 @@
        "gatewayPath":"v1/test"
       }
     ]]
+    ngx.req = { get_uri_args = function() return {} end}
     ngx.var = cjson.decode(ngxattrs)
     _G.ngx = ngx
     local securityObj = [[