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

Some fixes / improvements for Keycloak integration #310

Merged
merged 6 commits into from
Mar 20, 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Ability to use Redis DB and password via `REDIS_URL` [PR #303](https://github.com/3scale/apicast/pull/303)
- Ability to Authenticate against API using RHSSO and OpenID Connect [PR #283](https://github.com/3scale/apicast/pull/283)

### Fixed
- `http_ng` client supports auth passsed in the url, and default client options if the request options are missing for methods with body (POST, PUT, etc.) [PR #310](https://github.com/3scale/apicast/pull/310)

### Removed

- Removed support for sending Request logs [PR #296](https://github.com/3scale/apicast/pull/296)
Expand Down
16 changes: 10 additions & 6 deletions apicast/src/configuration_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,16 @@ function boot.init(configuration)
os.exit(0)
end

local keycloak_config = _M.run_external_command("keycloak")

if keycloak_config then
configuration.keycloak = cjson.decode(keycloak_config)
if keycloak.enabled() then
local keycloak_config
keycloak_config, err, code = _M.run_external_command("keycloak")
if keycloak_config then
ngx.log(ngx.DEBUG, 'downloaded keycloak configuration: ', keycloak_config)
configuration.keycloak = cjson.decode(keycloak_config)
else
-- TODO: consider exiting if there is problem with keycloak configuration
ngx.log(ngx.WARN, 'failed to load keycloak configuration, exiting (code ', code, ')\n', err)
end
end
end

Expand All @@ -128,8 +134,6 @@ end
function boot.init_worker(configuration)
local interval = ttl() or 0

configuration.keycloak = keycloak.load_configuration()

local function schedule(...)
local ok, err = ngx.timer.at(...)

Expand Down
4 changes: 4 additions & 0 deletions apicast/src/oauth/keycloak.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ local function get_public_key(http_client, endpoint)
return format_public_key(key)
end

function _M.enabled()
return env.get('RHSSO_ENDPOINT')
end

function _M.load_configuration(client)
local endpoint = env.get('RHSSO_ENDPOINT')

Expand Down
45 changes: 16 additions & 29 deletions apicast/src/proxy.lua
Original file line number Diff line number Diff line change
Expand Up @@ -266,38 +266,15 @@ function _M:set_backend_upstream(service)
ngx.var.backend_host = backend.host or server or ngx.var.backend_host
end


local function auth_oauth(proxy, service, usage, auth)
local credentials, err = proxy.oauth:transform_credentials(auth)

if err then
return error_authorization_failed(service)
end

credentials = encode_args(credentials)

return proxy:authorize(service, usage, credentials)
end

local function auth_key(proxy, service, usage, auth)
local credentials = encode_args(auth)

return proxy:authorize(service, usage, credentials)
end

function _M:call(host)
host = host or ngx.var.host
local service = ngx.ctx.service or self:set_service(host)

self:set_backend_upstream(service)

local authorize = auth_key

if service.backend_version == 'oauth' then
local o = oauth.new(self.configuration)
local f, params = oauth.call(o, service)

authorize = auth_oauth
self.oauth = o

if f then
Expand All @@ -308,11 +285,11 @@ function _M:call(host)

return function()
-- call access phase
return self:access(service, authorize)
return self:access(service)
end
end

function _M:access(service, authorize)
function _M:access(service)
local request = ngx.var.request -- NYI: return to lower frame

ngx.var.secret_token = service.secret_token
Expand All @@ -328,8 +305,7 @@ function _M:access(service, authorize)

insert(credentials, 1, service.id)

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

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

Expand All @@ -342,11 +318,22 @@ function _M:access(service, authorize)
local ctx = ngx.ctx

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

return authorize(self, service, usage, credentials)
if self.oauth then
credentials, err = self.oauth:transform_credentials(credentials)

if err then
return error_authorization_failed(service)
end
end

credentials = encode_args(credentials)
local usage = encode_args(usage_params)

return self:authorize(service, usage, credentials)
end


Expand Down
46 changes: 26 additions & 20 deletions apicast/src/resty/http_ng.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ local function merge(...)
return res
end

local function get_request_params(method, client, url, options)
local opts = {}
local scheme, user, pass, host, port, path = unpack(assert(resty_url.split(url)))
if port then host = concat({host, port}, ':') end

opts.headers = { ['Host'] = host }

if user or pass then
opts.headers.Authorization = "Basic " .. ngx.encode_base64(concat({ user or '', pass or '' }, ':'))
end

return {
url = concat({ scheme, '://', host, path or DEFAULT_PATH }, ''),
method = method,
options = merge(opts, rawget(client, 'options'), options),
client = client,
serializer = client.serializer or http.serializers.default
}
end

http.method = function(method, client)
assert(method)
assert(client)
Expand All @@ -64,23 +84,8 @@ http.method = function(method, client)

assert(url, 'url as first parameter is required')

local opts = {}
local scheme, user, pass, host, port, path = unpack(assert(resty_url.split(url)))
if port then host = concat({host, port}, ':') end

opts.headers = { ['Host'] = host }

if user or pass then
opts.headers.Authorization = "Basic " .. ngx.encode_base64(concat({ user or '', pass or '' }, ':'))
end

local req = http.request.new({
url = concat({ scheme, '://', host, path or DEFAULT_PATH }, ''),
method = method,
options = merge(opts, rawget(client, 'options'), options),
client = client,
serializer = client.serializer or http.serializers.default
})
local req_params = get_request_params(method, client, url, options)
local req = http.request.new(req_params)

return client.backend.send(req)
end
Expand All @@ -99,9 +104,10 @@ http.method_with_body = function(method, client)
assert(url, 'url as first parameter is required')
assert(body, 'body as second parameter is required')

local req = http.request.new{ url = url, method = method, body = body,
options = options, client = client,
serializer = client.serializer or http.serializers.default }
local req_params = get_request_params(method, client, url, options)
req_params.body = body
local req = http.request.new(req_params)

return client.backend.send(req)
end
end
Expand Down
10 changes: 10 additions & 0 deletions spec/keycloak_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,14 @@ describe('Keycloak', function()
assert.spy(_M.respond_with_error).was.called_with(401, 'invalid_client')
end)
end)

describe('.enabled', function()
it('is falsy if there is no RHSSO_ENDPOINT enviroment varialbe set', function()
assert.is.falsy(_M.enabled())
end)
it('if truty a non-nil value if RHSSO_ENDPOINT enviroment varialbe set', function()
env.set('RHSSO_ENDPOINT', 'http://www.example.com:80/auth/realms/test')
assert.is.truthy(_M.enabled())
end)
end)
end)
8 changes: 8 additions & 0 deletions spec/resty/http_ng_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ describe('http_ng', function()
http.get('http://example.com')
local last_request = assert(backend.last_request)

assert.equal(false, last_request.options.ssl.verify)
end)
it('can turn off ssl validation for methods with body', function()
http = http_ng.new{backend = backend, options = { ssl = { verify = false } } }

http.post('http://example.com', {})
local last_request = assert(backend.last_request)

assert.equal(false, last_request.options.ssl.verify)
end)
end)
Expand Down