facebook fun
diff --git a/scripts/lua/oauth/fake_facebook.lua b/scripts/lua/oauth/fake_facebook.lua
new file mode 100644
index 0000000..40f675c
--- /dev/null
+++ b/scripts/lua/oauth/fake_facebook.lua
@@ -0,0 +1,44 @@
+-- Copyright (c) 2016 IBM. All rights reserved.
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a
+-- copy of this software and associated documentation files (the "Software"),
+-- to deal in the Software without restriction, including without limitation
+-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
+-- and/or sell copies of the Software, and to permit persons to whom the
+-- Software is furnished to do so, subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in
+-- all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+-- DEALINGS IN THE SOFTWARE.
+
+local cjson = require 'cjson'
+local utils = require "lib/utils"
+
+function validateOAuthToken (token)
+
+ local headerName = utils.concatStrings({'http_', 'x-facebook-app-token'}):gsub("-", "_")
+
+ local facebookAppToken = ngx.var[headerName]
+ if facebookAppToken == nil then
+ request.err(401, 'Facebook requires you provide an app token to validate user tokens. Provide a X-Facebook-App-Token header')
+ return nil
+ end
+ if token == "token" and facebookAppToken == "app" then
+ return [[
+ {
+ "token":"good"
+ }
+ ]]
+ end
+ return nil
+end
+
+return validateOAuthToken
+
diff --git a/scripts/lua/policies/security/oauth2.lua b/scripts/lua/policies/security/oauth2.lua
index 89be891..b17d95a 100644
--- a/scripts/lua/policies/security/oauth2.lua
+++ b/scripts/lua/policies/security/oauth2.lua
@@ -34,19 +34,22 @@
local REDIS_PASS = os.getenv("REDIS_PASS")
-- Process the security object
--- @param securityObj security object from nginx conf file
+-- @param securityObj security object from nginx conf file
-- @return oauthId oauth identification
local _M = {}
-function process(securityObj)
- local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
+function process(securityObj)
+ local red = redis.init(REDIS_HOST, REDIS_PORT, REDIS_PASS, 1000)
local result = processWithRedis(red, securityObj)
redis.close(red)
return result
end
-function processWithRedis(red, securityObj)
-
+function processWithRedis(red, securityObj)
+
+ local headerName = utils.concatStrings({'http_', 'x-facebook-app-token'}):gsub("-", "_")
+ local facebookAppToken = ngx.var[headerName]
+
local accessToken = ngx.var['http_Authorization']
if accessToken == nil then
request.err(401, "No Authorization header provided")
@@ -55,37 +58,50 @@
local token = {}
local key = utils.concatStrings({"oauth:providers:", securityObj.provider, ":tokens:", accessToken})
-- If we haven't cached the token go to the oauth provider
- if not (redis.exists(red, key) == 1) then
- token = exchange(accessToken, securityObj.provider)
- else
- token = cjson.decode(redis.get(red, key))
+ if redis.exists(red, key) == 1 then
+ return cjson.decode(redis.get(red, key))
+ end
+
+ if securityObj.provider == 'facebook' or securityObj.provider == 'fake_facebook' then
+ if redis.exists(red, utils.concatStrings({key, facebookAppToken})) == 1 then
+ return cjson.decode(redis.get(red, utils.concatStrings({key, facebookAppToken})))
+ end
+ end
+
+ local token = exchange(accessToken, securityObj.provider)
+ if token == nil then
+ request.err(401, 'Token didn\'t work or provider doesn\'t support OpenID connect. ')
+ return nil
+ end
+
+ if token.error ~= nil then
+ request.err(401, 'Token didn\'t work or provider doesn\'t support OpenID connect. ')
+ return nil
end
- if token == nil or not (token.error == nil) then
- request.err(401, "Token didn't work or provider doesn't support OpenID connect.")
- return false
+ if securityObj.provider == 'facebook' or securityObj.provider == 'fake_facebook' then
+ key = utils.concatStrings({key, facebookAppToken})
end
+
redis.set(red, key, cjson.encode(token))
-
- if not token.expires == nil then
+
+ if token.expires ~= nil then
redis.expire(red, key, token.expires)
end
- return key
--- only check with the provider if we haven't cached the token.
-
+ return token
end
--- Exchange tokens with an oauth provider. Loads a provider based on configuration in the nginx.conf
-- @param token the accessToken passed in the authorization header of the routing request
-- @param provider the name of the provider we will load from a file. Currently supported google/github/facebook
-- @return the json object recieved from exchanging tokens with the provider
- function exchange(token, provider)
+ function exchange(token, provider)
-- exchange tokens with the provider
local loaded, provider = pcall(require, utils.concatStrings({'oauth/', provider}))
-
- if not loaded then
+
+ if not loaded then
request.err(500, 'Error loading OAuth provider authentication module')
- return
+ return
end
local token = provider(token)
-- cache the token
diff --git a/tests/scripts/lua/security.lua b/tests/scripts/lua/security.lua
index c5296d2..308a706 100644
--- a/tests/scripts/lua/security.lua
+++ b/tests/scripts/lua/security.lua
@@ -23,13 +23,13 @@
local fakeredis = require 'fakeredis'
local apikey = require 'policies/security/apiKey'
local oauth = require 'policies/security/oauth2'
-local cjson = require "cjson"
+local cjson = require "cjson"
-describe('API Key module', function()
- it('Checks an apiKey correctly', function()
- local red = fakeredis.new()
- local ngx = fakengx.new()
- local ngxattrs = cjson.decode([[
+describe('API Key module', function()
+ it('Checks an apiKey correctly', function()
+ local red = fakeredis.new()
+ local ngx = fakengx.new()
+ local ngxattrs = cjson.decode([[
{
"tenant":"abcd",
"gatewayPath":"v1/test",
@@ -48,11 +48,11 @@
red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
assert.same(key, 'a1234')
- end)
- it('Returns nil with a bad apikey', function()
- local red = fakeredis.new()
- local ngx = fakengx.new()
- local ngxattrs = cjson.decode([[
+ end)
+ it('Returns nil with a bad apikey', function()
+ local red = fakeredis.new()
+ local ngx = fakengx.new()
+ local ngxattrs = cjson.decode([[
{
"tenant":"abcd",
"gatewayPath":"v1/test",
@@ -70,11 +70,11 @@
red:hset('resources:abcd:v1/test', 'resources', '{"apiId":"bnez"}')
local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
assert.falsy(key)
- end)
- it('Checks for a key with a custom header', function()
- local red = fakeredis.new()
- local ngx = fakengx.new()
- local ngxattrs = cjson.decode([[
+ end)
+ it('Checks for a key with a custom header', function()
+ local red = fakeredis.new()
+ local ngx = fakengx.new()
+ local ngxattrs = cjson.decode([[
{
"tenant":"abcd",
"gatewayPath":"v1/test",
@@ -94,11 +94,11 @@
red:set('subscriptions:tenant:abcd:api:bnez:key:a1234', 'true')
local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
assert.same(key, 'a1234')
- end)
- it('Checks for a key with a custom header and hash configuration', function()
- local red = fakeredis.new()
- local ngx = fakengx.new()
- local ngxattrs = cjson.decode([[
+ end)
+ it('Checks for a key with a custom header and hash configuration', function()
+ local red = fakeredis.new()
+ local ngx = fakengx.new()
+ local ngxattrs = cjson.decode([[
{
"tenant":"abcd",
"gatewayPath":"v1/test",
@@ -119,12 +119,12 @@
red:set('subscriptions:tenant:abcd:api:bnez:key:fakehash', 'true')
local key = apikey.processWithRedis(red, securityObj, function() return "fakehash" end)
assert.same(key, 'fakehash')
- end)
-end)
-describe('OAuth security module', function()
+ end)
+end)
+describe('OAuth security module', function()
it('Exchanges a good secret', function ()
- local red = fakeredis.new()
- local token = "test"
+ local red = fakeredis.new()
+ local token = "test"
local ngxattrs = [[
{
"http_Authorization":"]] .. token .. [[",
@@ -133,7 +133,7 @@
}
]]
local ngx = fakengx.new()
- ngx.var = cjson.decode(ngxattrs)
+ ngx.var = cjson.decode(ngxattrs)
_G.ngx = ngx
local securityObj = [[
{
@@ -142,13 +142,13 @@
"scope":"resource"
}
]]
- local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+ local result = oauth.processWithRedis(red, cjson.decode(securityObj))
assert.same(red:exists('oauth:providers:mock:tokens:test'), 1)
assert(result)
end)
- it('Exchanges a bad token, doesn\'t cache it and returns false', function()
- local red = fakeredis.new()
- local token = "bad"
+ it('Exchanges a bad token, doesn\'t cache it and returns false', function()
+ local red = fakeredis.new()
+ local token = "bad"
local ngxattrs = [[
{
"http_Authorization":"]] .. token .. [[",
@@ -157,7 +157,7 @@
}
]]
local ngx = fakengx.new()
- ngx.var = cjson.decode(ngxattrs)
+ ngx.var = cjson.decode(ngxattrs)
_G.ngx = ngx
local securityObj = [[
{
@@ -170,10 +170,89 @@
assert.same(red:exists('oauth:providers:mock:tokens:bad'), 0)
assert.falsy(result)
end)
-end)
-describe('Client Secret Module', function()
+ it('Loads a facebook token from the cache without a valid app id', function()
+ local red = fakeredis.new()
+ local token = "test"
+ local ngxattrs = [[
+ {
+ "http_Authorization":"]] .. token .. [[",
+ "tenant":"1234",
+ "gatewayPath":"v1/test"
+ }
+ ]]
+ local ngx = fakengx.new()
+ ngx.var = cjson.decode(ngxattrs)
+ _G.ngx = ngx
+ local securityObj = [[
+ {
+ "type":"oauth2",
+ "provider":"fake_facebook",
+ "scope":"resource"
+ }
+ ]]
+ red:set('oauth:providers:fake_facebook:tokens:test', '{ "token":"good"}')
+ local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+ assert.truthy(result)
+ end)
+ it('Loads a facebook token from the cache with a valid app id', function()
+ local red = fakeredis.new()
+ local token = "test"
+ local appid = "app"
+ local ngxattrs = [[
+ {
+ "http_Authorization":"]] .. token .. [[",
+ "http_x_facebook_app_token":"]] .. appid .. [[",
+ "tenant":"1234",
+ "gatewayPath":"v1/test"
+ }
+ ]]
+ local ngx = fakengx.new()
+ ngx.var = cjson.decode(ngxattrs)
+ _G.ngx = ngx
+ local securityObj = [[
+ {
+ "type":"oauth2",
+ "provider":"fake_facebook",
+ "scope":"resource"
+ }
+ ]]
+ red:set('oauth:providers:fake_facebook:tokens:testapp', '{"token":"good"}')
+ local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+ assert.truthy(result)
+ end)
+
+ it('Exchanges a facebook token and sets it in redis correctly with the appid.', function()
+ local red = fakeredis.new()
+ local token = "token"
+ local appid = "app"
+ local ngxattrs = [[
+ {
+ "http_Authorization":"]] .. token .. [[",
+ "http_x_facebook_app_token":"]] .. appid .. [[",
+ "tenant":"1234",
+ "gatewayPath":"v1/test"
+ }
+ ]]
+ local ngx = fakengx.new()
+ ngx.var = cjson.decode(ngxattrs)
+ _G.ngx = ngx
+ local result = oauth.processWithRedis(red, cjson.decode(ngxattrs))
+ local securityObj = [[
+ {
+ "type":"oauth2",
+ "provider":"fake_facebook",
+ "scope":"resource"
+ }
+ ]]
+ local result = oauth.processWithRedis(red, cjson.decode(securityObj))
+ assert.truthy(result)
+ assert.truthy(red:exists('oauth:providers:fake_facebook:tokens:tokenapp') == 1)
+ assert.truthy(red:exists('oauth:providers:fake_facebook:tokens:token') == 0)
+ end)
+end)
+describe('Client Secret Module', function()
local clientSecret = require 'policies/security/clientSecret'
- it('Validates a client secret pair with default names', function()
+ it('Validates a client secret pair with default names', function()
local ngx = fakengx.new()
local red = fakeredis.new()
local ngxattrs = [[
@@ -184,7 +263,7 @@
"gatewayPath":"v1/test"
}
]]
- ngx.var = cjson.decode(ngxattrs)
+ ngx.var = cjson.decode(ngxattrs)
_G.ngx = ngx
local securityObj = [[
{
@@ -193,12 +272,12 @@
}
]]
red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
- local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
+ local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
assert(result)
- end)
- it('Validates a client secret pair with new names', function()
+ end)
+ it('Validates a client secret pair with new names', function()
local ngx = fakengx.new()
- local red = fakeredis.new()
+ local red = fakeredis.new()
local ngxattrs = [[
{
"http_test_id":"abcd",
@@ -217,11 +296,11 @@
"secretFieldName":"test-secret"
}
]]
- red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
- local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
+ red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
+ local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
assert(result)
- end)
- it('Doesn\'t work without a client id', function()
+ end)
+ it('Doesn\'t work without a client id', function()
local ngx = fakengx.new()
local red = fakeredis.new()
local ngxattrs = [[
@@ -231,7 +310,7 @@
"gatewayPath":"v1/test"
}
]]
- ngx.var = cjson.decode(ngxattrs)
+ ngx.var = cjson.decode(ngxattrs)
_G.ngx = ngx
local securityObj = [[
{
@@ -240,7 +319,7 @@
}
]]
end)
- it('Doesn\'t work without a Client Secret', function()
+ it('Doesn\'t work without a Client Secret', function()
local ngx = fakengx.new()
local red = fakeredis.new()
local ngxattrs = [[
@@ -250,7 +329,7 @@
"gatewayPath":"v1/test"
}
]]
- ngx.var = cjson.decode(ngxattrs)
+ ngx.var = cjson.decode(ngxattrs)
_G.ngx = ngx
local securityObj = [[
{
@@ -259,7 +338,7 @@
}
]]
red:set("subscriptions:tenant:1234:resource:v1/test:clientsecret:abcd:fakehash", "true")
- local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
+ local result = clientSecret.processWithHashFunction(red, cjson.decode(securityObj), function() return "fakehash" end)
assert.falsy(result)
- end)
+ end)
end)