Skip to content

Commit

Permalink
[proxy] handle partial credentials
Browse files Browse the repository at this point in the history
- part of the credentials can be missing
- return proper error message
  • Loading branch information
mikz committed Aug 30, 2017
1 parent 747905b commit 8b6fd46
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Crash on empty OIDC Issuer endpoint [PR #408](https://github.com/3scale/apicast/pull/408)

### Fixes

- Handle partial credentials [PR #409](https://github.com/3scale/apicast/pull/409)

### Changed

- `THREESCALE\_DEPLOYMENT\_ENV` defaults to `production` [PR #406](https://github.com/3scale/apicast/pull/406)
Expand Down
14 changes: 11 additions & 3 deletions apicast/src/configuration/service.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ local function read_http_header(name)
return ngx.var['http_' .. normalized]
end

local function tuple_mt(size)
return { __len = function() return size end }
end

local credentials_v2_mt = tuple_mt(2)
local credentials_v1_mt = tuple_mt(1)
local credentials_oauth_mt = tuple_mt(1)

local backend_version_credentials = { }

function backend_version_credentials.version_1(config)
Expand All @@ -74,7 +82,7 @@ function backend_version_credentials.version_1(config)
-- @field 1 User Key
-- @field user_key User Key
-- @table credentials_v1
return { user_key, user_key = user_key }
return setmetatable({ user_key, user_key = user_key }, credentials_v1_mt)
end

function backend_version_credentials.version_2(config)
Expand Down Expand Up @@ -113,7 +121,7 @@ function backend_version_credentials.version_2(config)
-- @field app_id App ID
-- @field app_key App Key
-- @table credentials_v2
return { app_id, app_key, app_id = app_id, app_key = app_key }
return setmetatable({ app_id, app_key, app_id = app_id, app_key = app_key }, credentials_v2_mt)
end

function backend_version_credentials.version_oauth(config)
Expand Down Expand Up @@ -143,7 +151,7 @@ function backend_version_credentials.version_oauth(config)
-- @field 1 Access Token
-- @field access_token Access Token
-- @table credentials_oauth
return { access_token, access_token = access_token }
return setmetatable({ access_token, access_token = access_token }, credentials_oauth_mt)
end

-- This table can be used with `table.concat` to serialize
Expand Down
33 changes: 19 additions & 14 deletions apicast/src/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ local assert = assert
local type = type
local next = next
local insert = table.insert

local concat = table.concat
local gsub = string.gsub
local tonumber = tonumber
Expand Down Expand Up @@ -64,7 +63,7 @@ local function error_no_credentials(service)
ngx.status = service.auth_missing_status
ngx.header.content_type = service.auth_missing_headers
ngx.print(service.error_auth_missing)
ngx.exit(ngx.HTTP_OK)
return ngx.exit(ngx.HTTP_OK)
end

local function error_authorization_failed(service)
Expand All @@ -73,7 +72,7 @@ local function error_authorization_failed(service)
ngx.status = service.auth_failed_status
ngx.header.content_type = service.auth_failed_headers
ngx.print(service.error_auth_failed)
ngx.exit(ngx.HTTP_OK)
return ngx.exit(ngx.HTTP_OK)
end

local function error_no_match(service)
Expand All @@ -83,14 +82,14 @@ local function error_no_match(service)
ngx.status = service.no_match_status
ngx.header.content_type = service.no_match_headers
ngx.print(service.error_no_match)
ngx.exit(ngx.HTTP_OK)
return ngx.exit(ngx.HTTP_OK)
end

local function error_service_not_found(host)
ngx.status = 404
ngx.print('')
ngx.log(ngx.WARN, 'could not find service for host: ', host)
ngx.exit(ngx.status)
return ngx.exit(ngx.status)
end
-- End Error Codes

Expand Down Expand Up @@ -320,32 +319,38 @@ function _M:access(service)

local credentials, err = service:extract_credentials()

if not credentials or #credentials == 0 then
if err then
ngx.log(ngx.WARN, "cannot get credentials: ", err)
end
if not credentials then
ngx.log(ngx.WARN, "cannot get credentials: ", err or 'unknown error')
return error_no_credentials(service)
end

insert(credentials, 1, service.id)

local _, matched_patterns, usage_params = service:extract_usage(request)

ngx.var.cached_key = concat(credentials, ':')
local cached_key = { service.id }

-- remove integer keys for serialization
-- as ngx.encode_args can't serialize integer keys
-- and verify all the keys exist
for i=1,#credentials do
credentials[i] = nil
local val = credentials[i]
if not val then
return error_no_credentials(service)
else
credentials[i] = nil
end

insert(cached_key, val)
end

local ctx = ngx.ctx
local var = ngx.var

-- save those tables in context so they can be used in the backend client
ctx.usage = usage_params
ctx.credentials = credentials
ctx.matched_patterns = matched_patterns

var.cached_key = concat(cached_key, ':')

local ttl

if self.oauth then
Expand Down
15 changes: 15 additions & 0 deletions spec/proxy_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ describe('Proxy', function()
end)
end)

describe(':access', function()
local service
before_each(function()
ngx.var = { backend_endpoint = 'http://localhost:1853' }
service = Service.new({ extract_usage = function() end })
end)

it('works with part of the credentials', function()
service.credentials = { location = 'headers' }
service.backend_version = 2
ngx.var.http_app_key = 'key'
assert.falsy(proxy:access(service))
end)
end)

it('has post_action function', function()
assert.truthy(proxy.post_action)
assert.same('function', type(proxy.post_action))
Expand Down
26 changes: 26 additions & 0 deletions t/003-apicast.t
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ GET /
credentials missing!
--- error_code: 401
=== TEST 2: authentication (part of) credentials missing
The message is configurable as well as the status.
--- http_config
lua_package_path "$TEST_NGINX_LUA_PATH";
init_by_lua_block {
require('configuration_loader').mock({
services = {
{
backend_version = 2,
proxy = {
error_auth_missing = 'credentials missing!',
error_status_auth_missing = 401
}
}
}
})
}
--- config
include $TEST_NGINX_BACKEND_CONFIG;
include $TEST_NGINX_APICAST_CONFIG;
--- request
GET /?app_key=42
--- response_body chomp
credentials missing!
--- error_code: 401
=== TEST 2: no mapping rules matched
The message is configurable and status also.
--- http_config
Expand Down

0 comments on commit 8b6fd46

Please sign in to comment.