Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

don't call post action for oauth routes #343

Merged
merged 2 commits into from
Mar 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Use `RESOLVER` before falling back to `resolv.conf` [PR #324](https://github.com/3scale/apicast/pull/324)
- Improve error logging when failing to download configuration [PR #335](https://github.com/3scale/apicast/pull/325)
- Service hostnames are normalized to lower case [PR #336](https://github.com/3scale/apicast/pull/326)
- Don't attempt to perform post\_action when request was handled without authentication [PR #343](https://github.com/3scale/apicast/pull/343)

### Fixed

Expand Down
47 changes: 34 additions & 13 deletions apicast/src/apicast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ local mt = {
__index = _M
}

-- So there is no way to use ngx.ctx between request and post_action.
-- We somehow need to share the instance of the proxy between those.
-- This table is used to store the proxy object with unique reqeust id key
-- and removed in the post_action.
local post_action_proxy = {}

--- This is called when APIcast boots the master process.
function _M.new()
return setmetatable({ configuration = configuration_store.new() }, mt)
return setmetatable({
configuration = configuration_store.new(),
-- So there is no way to use ngx.ctx between request and post_action.
-- We somehow need to share the instance of the proxy between those.
-- This table is used to store the proxy object with unique reqeust id key
-- and removed in the post_action. Because it there is just one instance
-- of this module in each worker.
post_action_proxy = {}
}, mt)
end

function _M:init()
Expand Down Expand Up @@ -65,26 +67,45 @@ function _M:rewrite()
ngx.ctx.proxy = p
end

function _M.post_action()
function _M:post_action()
local request_id = ngx.var.original_request_id
local post_action_proxy = self.post_action_proxy

if not post_action_proxy then
return nil, 'not initialized'
end

local p = ngx.ctx.proxy or post_action_proxy[request_id]

post_action_proxy[request_id] = nil

if p then
p:post_action()
return p:post_action()
else
ngx.log(ngx.INFO, 'could not find proxy for request id: ', request_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This info message looks a bit alarming, like if it was supposed to find proxy, but couldn't, while most of the times it is absolutely fine and deliberate. Maybe say "skipping post_action, as no proxy was registered for the request id", and move it to DEBUG level?...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was thinking about that. INFO is first opt-in level so it is not shown by default.

I guess this should be part of some major log refactoring as there is lots of duplicated / too verbose information already.

return nil, 'no proxy for request'
end
end

function _M.access()
function _M:access()
local p = ngx.ctx.proxy
local fun = p:call() -- proxy:access() or oauth handler
local post_action_proxy = self.post_action_proxy

local ok, err = fun()
if not post_action_proxy then
return nil, 'not initialized'
end

local access, handler = p:call() -- proxy:access() or oauth handler

post_action_proxy[ngx.var.original_request_id] = p
local ok, err

if access then
ok, err = access()
post_action_proxy[ngx.var.original_request_id] = p
elseif handler then
ok, err = handler()
-- no proxy because that would trigger post action
end

return ok, err
end
Expand Down
16 changes: 15 additions & 1 deletion apicast/src/proxy.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
------------
-- Proxy
-- Module that handles the request authentication and proxying upstream.
--
-- @module proxy
-- @author mikz
-- @license Apache License Version 2.0

local env = require 'resty.env'
local custom_config = env.get('APICAST_CUSTOM_CONFIG')
local configuration_store = require 'configuration_store'
Expand Down Expand Up @@ -266,6 +274,12 @@ function _M:set_backend_upstream(service)
ngx.var.backend_host = backend.host or server or ngx.var.backend_host
end

-----
-- call the proxy and return a handler function
-- that will perform an action based on the path and backend version
-- @string host optional hostname, uses `ngx.var.host` otherwise
-- @treturn nil|function access function (when the request needs to be authenticated with this)
-- @treturn nil|function handler function (when the request is not authenticated and has some own action)
function _M:call(host)
host = host or ngx.var.host
local service = ngx.ctx.service or self:set_service(host)
Expand All @@ -279,7 +293,7 @@ function _M:call(host)

if f then
ngx.log(ngx.DEBUG, 'apicast oauth flow')
return function() return f(params) end
return nil, function() return f(params) end
end
end

Expand Down
1 change: 1 addition & 0 deletions doc/config.ld
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
file = {
'../apicast/src/proxy.lua',
'../apicast/src/configuration/service.lua',
'../apicast/src/resty/env.lua',
'../apicast/src/resty/http_ng.lua',
Expand Down
51 changes: 48 additions & 3 deletions spec/apicast_spec.lua
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
local apicast = require 'apicast'
local _M = require 'apicast'

describe('APIcast module', function()

it('has a name', function()
assert.truthy(apicast._NAME)
assert.truthy(_M._NAME)
end)

it('has a version', function()
assert.truthy(apicast._VERSION)
assert.truthy(_M._VERSION)
end)

describe(':access', function()

local apicast

before_each(function()
apicast = _M.new()
ngx.ctx.proxy = {}
end)

it('triggers post action when access phase succeeds', function()
ngx.var = { original_request_id = 'foobar' }

stub(ngx.ctx.proxy, 'call', function()
return function() return 'ok' end
end)

stub(ngx.ctx.proxy, 'post_action', function()
return 'post_ok'
end)

local ok, err = apicast:access()

assert.same('ok', ok)
assert.falsy(err)

assert.same('post_ok', apicast:post_action())
end)

it('skips post action when access phase not executed', function()
stub(ngx.ctx.proxy, 'call', function()
-- return nothing for example
end)

local ok, err = apicast:access()

assert.falsy(ok)
assert.falsy(err)

ngx.ctx.proxy = nil -- in post_action the ctx is not shared
ngx.var = { original_request_id = 'foobar' }

assert.equal(nil, apicast:post_action())
end)
end)
end)
34 changes: 28 additions & 6 deletions spec/proxy_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,36 @@ describe('Proxy', function()
assert.same('function', type(proxy.access))
end)

it('has authorize function after call', function()
ngx.var = { backend_endpoint = 'http://localhost:1853' }
configuration:add({ id = 42, hosts = { 'localhost' }})
describe(':call', function()
before_each(function()
ngx.var = { backend_endpoint = 'http://localhost:1853' }
configuration:add({ id = 42, hosts = { 'localhost' }})
end)

it('has authorize function after call', function()
proxy:call('localhost')

proxy:call('localhost')
assert.truthy(proxy.authorize)
assert.same('function', type(proxy.authorize))
end)

assert.truthy(proxy.authorize)
assert.same('function', type(proxy.authorize))
it('returns access function', function()
local access = proxy:call('localhost')

assert.same('function', type(access))
end)

it('returns oauth handler when matches oauth route', function()
local service = configuration:find_by_id(42)
service.backend_version = 'oauth'
stub(ngx.req, 'get_method', function() return 'GET' end)
ngx.var.uri = '/authorize'

local access, handler = proxy:call('localhost')

assert.equal(nil, access)
assert.same('function', type(handler))
end)
end)

it('has post_action function', function()
Expand Down