Fix App ID bug, add unit tests (#357)

diff --git a/Dockerfile.test.unit b/Dockerfile.test.unit
index 7038c9f..b10e661 100644
--- a/Dockerfile.test.unit
+++ b/Dockerfile.test.unit
@@ -25,11 +25,12 @@
 FROM alpine:3.9
 
 ENV CJOSE_VERSION=0.5.1
+ENV STEP_VERSION=0.11.0
 
 RUN apk update && \
     apk add \
-    gcc tar zlib wget make musl-dev g++ curl \
-    libtool readline luajit luajit-dev unzip \
+    gcc tar zlib zlib-dev wget make musl-dev g++ curl \
+    libtool readline luajit luajit-dev unzip coreutils \
     openssl openssl-dev git jansson jansson-dev
 
 WORKDIR /tmp
@@ -49,9 +50,35 @@
     && make && make install \
     && rm -rf /tmp/api-gateway
 
-COPY . /etc/api-gateway
+RUN echo " ... installing step cli ... " \
+    && mkdir -p /tmp/step \
+    && curl -L https://github.com/smallstep/cli/releases/download/v${STEP_VERSION}/step_${STEP_VERSION}_linux_amd64.tar.gz -o /tmp/step/step_${STEP_VERSION}.tar.gz \
+    && tar -xf /tmp/step/step_${STEP_VERSION}.tar.gz -C /tmp/step \
+    && cd /tmp/step/step_${STEP_VERSION} \
+    && mv ./bin/step /usr/bin/step
 
+RUN echo " ... generating JWK/JWT ... " \
+    && step crypto jwk create /tmp/pub.jwk.json /tmp/prv.jwk.json -f --insecure --no-password --kty RSA --kid test-jwk \
+    && cat /tmp/pub.jwk.json | step crypto jwk keyset add /tmp/jwk.json \
+    && export JWT_EXPIRY=$(date -d "+10 days" +%s) \
+    && echo '{"email":"testuser@openwhisk.apache.org"}' | step crypto jwt sign --iss https://openwhisk.apache.org/apigateway --sub "test user" --exp $JWT_EXPIRY --aud tests --key /tmp/prv.jwk.json > /tmp/token.jwt
+
+RUN mkdir -p /etc/api-gateway/tests
 WORKDIR /etc/api-gateway/tests
-RUN ./install-deps.sh
+
+# Install Lua dependencies
+RUN luarocks install busted \
+    && luarocks install luacov \
+    && mkdir -p lua_modules \
+    && luarocks install --tree=lua_modules lua-cjson \
+    && luarocks install --tree=lua_modules luasocket \
+    && luarocks install --tree=lua_modules sha1 \
+    && luarocks install --tree=lua_modules md5 \
+    && luarocks install --tree=lua_modules net-url \
+    && luarocks install --tree=lua_modules luafilesystem \
+    && luarocks install --tree=lua_modules lua-resty-http 0.10 \
+    && luarocks install --tree=lua_modules https://github.com/mhamann/lua-resty-cjose/raw/master/lua-resty-cjose-0.5-0.rockspec
+
+COPY . /etc/api-gateway
 
 CMD sh run-tests.sh
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 55e5e01..3d0a5d8 100644
--- a/Makefile
+++ b/Makefile
@@ -34,10 +34,6 @@
 docker-ssh:
 	docker run -ti --entrypoint='bash' ${RUNTIME}
 
-.PHONY: test-build
-test-build:
-	cd tests; ./install-deps.sh
-
 .PHONY: profile-build
 profile-build:
 	./build_profiling.sh
diff --git a/scripts/lua/oauth/app-id.lua b/scripts/lua/oauth/app-id.lua
index a756f64..b594def 100644
--- a/scripts/lua/oauth/app-id.lua
+++ b/scripts/lua/oauth/app-id.lua
@@ -14,7 +14,6 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-
 local request = require 'lib/request'
 local cjson = require 'cjson'
 local utils = require 'lib/utils'
@@ -29,6 +28,7 @@
 end
 
 local function fetchJWKs(tenantId)
+  local httpc = http.new()
   local keyUrl = utils.concatStrings({APPID_PKURL, tenantId, '/publickeys'})
   local request_options = {
     headers = {
@@ -42,7 +42,6 @@
 function _M.process(dataStore, token, securityObj)
   local cache_key = 'appid_' .. securityObj.tenantId
   local result = dataStore:getOAuthToken(cache_key, token)
-  local httpc = http.new()
   local token_obj
 
   -- Was the token in the cache?
@@ -53,9 +52,10 @@
   end
 
   -- Cache miss. Proceed to validate the token
-  local res, err = fetchJWKs
-  if err or res.status ~= 200 then
+  local res, err = fetchJWKs(securityObj.tenantId)
+  if err ~= nil or not res or res.status ~= 200 then
     request.err(500, 'An error occurred while fetching the App ID JWK configuration: ' .. err or res.body)
+    return nil
   end
 
   local key
@@ -81,7 +81,8 @@
 
   -- keep token in cache until it expires
   local ttl = expireTime - os.time()
-  dataStore:saveOAuthToken(cache_key, token, cjson.encode(token_obj), ttl)
+  local encodedToken = cjson.encode(token_obj)
+  dataStore:saveOAuthToken(cache_key, token, encodedToken, ttl)
   return token_obj
 end
 
diff --git a/tests/install-deps.sh b/tests/install-deps.sh
deleted file mode 100755
index 156cb3f..0000000
--- a/tests/install-deps.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Install global dependencies
-luarocks install busted
-luarocks install luacov
-# Install test dependencies
-mkdir -p lua_modules
-luarocks install --tree=lua_modules lua-cjson
-luarocks install --tree=lua_modules luabitop
-luarocks install --tree=lua_modules luasocket
-luarocks install --tree=lua_modules sha1
-luarocks install --tree=lua_modules md5
-luarocks install --tree=lua_modules net-url
-luarocks install --tree=lua_modules luafilesystem
-luarocks install --tree=lua_modules lua-resty-http 0.10
-luarocks install --tree=lua_modules https://github.com/mhamann/lua-resty-cjose/raw/master/lua-resty-cjose-0.5-0.rockspec
diff --git a/tests/run-tests.sh b/tests/run-tests.sh
index 3a90230..817f831 100755
--- a/tests/run-tests.sh
+++ b/tests/run-tests.sh
@@ -16,5 +16,9 @@
 # limitations under the License.
 #
 
+# Grab pre-generated JWT for use in tests
+export OAUTH_TEST_JWT=$(cat /tmp/token.jwt)
+export OAUTH_TEST_JWK=$(cat /tmp/jwk.json)
+
 # Run unit tests
 busted --output=TAP --helper=set_paths --pattern=.lua scripts
diff --git a/tests/scripts/lua/security.lua b/tests/scripts/lua/security.lua
index e449ff9..59b244e 100644
--- a/tests/scripts/lua/security.lua
+++ b/tests/scripts/lua/security.lua
@@ -269,6 +269,48 @@
     local result = oauth.process(dataStore, cjson.decode(securityObj))
     assert.truthy(result)
   end)
+
+  it('Successfully fetches App ID JWK keys and validates token', function()
+    local red = fakeredis.new()
+    -- Mock red.expire w/ a no-op to avoid a seg fault
+    red.expire = function(arg)
+      return {}, nil
+    end
+    local ds = require "lib/dataStore"
+    local dataStore = ds.initWithDriver(red)
+    local token = os.getenv("OAUTH_TEST_JWT")
+    local appid = "app"
+    local ngxattrs = [[
+      {
+        "http_Authorization":"]] .. token .. [[",
+        "tenant":"1234",
+        "gatewayPath":"v1/test"
+      }
+    ]]
+    local ngx = fakengx.new()
+    ngx.config = { ngx_lua_version = 'test' }
+    ngx.var = cjson.decode(ngxattrs)
+    _G.ngx = ngx
+    -- Mock http lib request to return the "right" values
+    local http = require 'resty.http'
+    http.request_uri = function (url, params)
+      local res = {}
+      res.status = 200
+      res.body = os.getenv("OAUTH_TEST_JWK")
+      return res, nil
+    end
+
+    local securityObj = [[
+      {
+        "type":"oauth2",
+        "provider":"app-id",
+        "tenantId": "tenant1",
+        "scope":"api"
+      }
+    ]]
+    local result = oauth.process(dataStore, cjson.decode(securityObj))
+    assert.truthy(result)
+  end)
 end)
 describe('Client Secret Module', function()
   local clientSecret = require 'policies/security/clientSecret'