From 05587913fcba0dc6b0c09f5c97e2ee945484927b Mon Sep 17 00:00:00 2001 From: emoj Date: Tue, 9 Apr 2019 09:51:32 -0400 Subject: [PATCH] added anonymous support compatible with kong 1.0.X --- kong/plugins/oidc/handler.lua | 89 +++++++++++++++++++++++++---------- kong/plugins/oidc/schema.lua | 59 ++++++++++++++--------- 2 files changed, 101 insertions(+), 47 deletions(-) diff --git a/kong/plugins/oidc/handler.lua b/kong/plugins/oidc/handler.lua index 53bd2c7f..2eed9834 100644 --- a/kong/plugins/oidc/handler.lua +++ b/kong/plugins/oidc/handler.lua @@ -4,12 +4,16 @@ local utils = require("kong.plugins.oidc.utils") local filter = require("kong.plugins.oidc.filter") local session = require("kong.plugins.oidc.session") -local singletons = require "kong.singletons" local constants = require "kong.constants" -local responses = require "kong.tools.responses" + +local kong = kong OidcHandler.PRIORITY = 1000 +local function internal_server_error(err) + kong.log.err(err) + return kong.response.exit(500, { message = "An unexpected error occurred" }) +end function OidcHandler:new() OidcHandler.super.new(self, "oidc") @@ -18,7 +22,7 @@ end function OidcHandler:access(config) OidcHandler.super.access(self) - if ngx.ctx.authenticated_credential and config.anonymous ~= "" then + if config.anonymous and kong.client.get_credential() then -- we're already authenticated, and we're configured for using anonymous, -- hence we're in a logical OR between auth methods and we're already done. return @@ -69,19 +73,20 @@ function make_oidc(oidcConfig) ngx.log(ngx.DEBUG, "Entering recovery page: " .. oidcConfig.recovery_page_path) ngx.redirect(oidcConfig.recovery_page_path) end - if oidcConfig.anonymous ~= "" then + if oidcConfig.anonymous then -- get anonymous user - local consumer_cache_key = singletons.db.consumers:cache_key(oidcConfig.anonymous) - local consumer, err = singletons.cache:get(consumer_cache_key, nil, - load_consumer_into_memory, - oidcConfig.anonymous, true) + local consumer_cache_key = kong.db.consumers:cache_key(oidcConfig.anonymous) + local consumer, err = kong.cache:get(consumer_cache_key, nil, + load_consumer_into_memory, + oidcConfig.anonymous, true) if err then - return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) + return internal_server_error(err) end + set_consumer(consumer, nil, nil) else - utils.exit(500, err, ngx.HTTP_INTERNAL_SERVER_ERROR) + return kong.response.exit(err.status, err.message, err.headers) end end return res @@ -93,19 +98,20 @@ function introspect(oidcConfig) if err then if oidcConfig.bearer_only == "yes" then ngx.header["WWW-Authenticate"] = 'Bearer realm="' .. oidcConfig.realm .. '",error="' .. err .. '"' - if oidcConfig.anonymous ~= "" then + if oidcConfig.anonymous then -- get anonymous user - local consumer_cache_key = singletons.db.consumers:cache_key(oidcConfig.anonymous) - local consumer, err = singletons.cache:get(consumer_cache_key, nil, - load_consumer_into_memory, - oidcConfig.anonymous, true) + local consumer_cache_key = kong.db.consumers:cache_key(oidcConfig.anonymous) + local consumer, err = kong.cache:get(consumer_cache_key, nil, + load_consumer_into_memory, + oidcConfig.anonymous, true) if err then - return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) + return internal_server_error(err) end + set_consumer(consumer, nil, nil) else - utils.exit(ngx.HTTP_UNAUTHORIZED, err, ngx.HTTP_UNAUTHORIZED) + return kong.response.exit(err.status, err.message, err.headers) end end @@ -120,17 +126,48 @@ end -- TESTING local function set_consumer(consumer, credential, token) - ngx_set_header(constants.HEADERS.CONSUMER_ID, consumer.id) - ngx_set_header(constants.HEADERS.CONSUMER_CUSTOM_ID, consumer.custom_id) - ngx_set_header(constants.HEADERS.CONSUMER_USERNAME, consumer.username) - ngx.ctx.authenticated_consumer = consumer + local set_header = kong.service.request.set_header + local clear_header = kong.service.request.clear_header + + if consumer and consumer.id then + set_header(constants.HEADERS.CONSUMER_ID, consumer.id) + else + clear_header(constants.HEADERS.CONSUMER_ID) + end + + if consumer and consumer.custom_id then + set_header(constants.HEADERS.CONSUMER_CUSTOM_ID, consumer.custom_id) + else + clear_header(constants.HEADERS.CONSUMER_CUSTOM_ID) + end + + if consumer and consumer.username then + set_header(constants.HEADERS.CONSUMER_USERNAME, consumer.username) + else + clear_header(constants.HEADERS.CONSUMER_USERNAME) + end + + kong.client.authenticate(consumer, credential) + if credential then - ngx_set_header("x-authenticated-scope", token.scope) - ngx_set_header("x-authenticated-userid", token.authenticated_userid) - ngx.ctx.authenticated_credential = credential - ngx_set_header(constants.HEADERS.ANONYMOUS, nil) -- in case of auth plugins concatenation + if token.scope then + set_header("x-authenticated-scope", token.scope) + else + clear_header("x-authenticated-scope") + end + + if token.authenticated_userid then + set_header("x-authenticated-userid", token.authenticated_userid) + else + clear_header("x-authenticated-userid") + end + + clear_header(constants.HEADERS.ANONYMOUS) -- in case of auth plugins concatenation + else - ngx_set_header(constants.HEADERS.ANONYMOUS, true) + set_header(constants.HEADERS.ANONYMOUS, true) + clear_header("x-authenticated-scope") + clear_header("x-authenticated-userid") end end diff --git a/kong/plugins/oidc/schema.lua b/kong/plugins/oidc/schema.lua index 177b46b4..a7c04846 100644 --- a/kong/plugins/oidc/schema.lua +++ b/kong/plugins/oidc/schema.lua @@ -1,24 +1,41 @@ +local typedefs = require "kong.db.schema.typedefs" + +local function validate_flows(config) + + return true + +end + return { - no_consumer = true, + name = "oidc", fields = { - anonymous = { type = "string", uuid = true, legacy = true }, - client_id = { type = "string", required = true }, - client_secret = { type = "string", required = true }, - discovery = { type = "string", required = true, default = "https://.well-known/openid-configuration" }, - introspection_endpoint = { type = "string", required = false }, - timeout = { type = "number", required = false }, - introspection_endpoint_auth_method = { type = "string", required = false }, - bearer_only = { type = "string", required = true, default = "no" }, - realm = { type = "string", required = true, default = "kong" }, - redirect_uri_path = { type = "string" }, - scope = { type = "string", required = true, default = "openid" }, - response_type = { type = "string", required = true, default = "code" }, - ssl_verify = { type = "string", required = true, default = "no" }, - token_endpoint_auth_method = { type = "string", required = true, default = "client_secret_post" }, - session_secret = { type = "string", required = false }, - recovery_page_path = { type = "string" }, - logout_path = { type = "string", required = false, default = '/logout' }, - redirect_after_logout_uri = { type = "string", required = false, default = '/' }, - filters = { type = "string" } - } + { consumer = typedefs.no_consumer }, + { run_on = typedefs.run_on_first }, + { config = { + type = "record", + fields = { + {anonymous = { type = "string", uuid = true, legacy = true }}, + {client_id = { type = "string", required = true }}, + {client_secret = { type = "string", required = true }}, + {discovery = { type = "string", required = true, default = "https://.well-known/openid-configuration" }}, + {introspection_endpoint = { type = "string", required = false }}, + {timeout = { type = "number", required = false }}, + {introspection_endpoint_auth_method = { type = "string", required = false }}, + {bearer_only = { type = "string", required = true, default = "no" }}, + {realm = { type = "string", required = true, default = "kong" }}, + {redirect_uri_path = { type = "string" }}, + {scope = { type = "string", required = true, default = "openid" }}, + {response_type = { type = "string", required = true, default = "code" }}, + {ssl_verify = { type = "string", required = true, default = "no" }}, + {token_endpoint_auth_method = { type = "string", required = true, default = "client_secret_post" }}, + {session_secret = { type = "string", required = false }}, + {recovery_page_path = { type = "string" }}, + {logout_path = { type = "string", required = false, default = '/logout' }}, + {redirect_after_logout_uri = { type = "string", required = false, default = '/' }}, + {filters = { type = "string" }} + }, + custom_validator = validate_flows, + }, + }, + }, }