diff --git a/kong/plugins/basic-auth/access.lua b/kong/plugins/basic-auth/access.lua index 376ec34467bd..5206e0f23077 100644 --- a/kong/plugins/basic-auth/access.lua +++ b/kong/plugins/basic-auth/access.lua @@ -1,13 +1,16 @@ local crypto = require "kong.plugins.basic-auth.crypto" local constants = require "kong.constants" -local responses = require "kong.tools.responses" -local ngx_set_header = ngx.req.set_header -local ngx_get_headers = ngx.req.get_headers -local ngx_re_match = ngx.re.match + +local decode_base64 = ngx.decode_base64 +local re_gmatch = ngx.re.gmatch +local re_match = ngx.re.match +local kong = kong + local realm = 'Basic realm="' .. _KONG._NAME .. '"' + local _M = {} @@ -19,35 +22,34 @@ local _M = {} -- @param {table} conf Plugin config -- @return {string} public_key -- @return {string} private_key -local function retrieve_credentials(request, header_name, conf) +local function retrieve_credentials(header_name, conf) local username, password - local authorization_header = request.get_headers()[header_name] + local authorization_header = kong.request.get_header(header_name) if authorization_header then - local iterator, iter_err = ngx.re.gmatch(authorization_header, "\\s*[Bb]asic\\s*(.+)") + local iterator, iter_err = re_gmatch(authorization_header, "\\s*[Bb]asic\\s*(.+)") if not iterator then - ngx.log(ngx.ERR, iter_err) + kong.log.err(iter_err) return end local m, err = iterator() if err then - ngx.log(ngx.ERR, err) + kong.log.err(err) return end if m and m[1] then - local decoded_basic = ngx.decode_base64(m[1]) + local decoded_basic = decode_base64(m[1]) if decoded_basic then - local basic_parts, err = ngx_re_match(decoded_basic, - "([^:]+):(.*)", "oj") + local basic_parts, err = re_match(decoded_basic, "([^:]+):(.*)", "oj") if err then - ngx.log(ngx.ERR, err) + kong.log.err(err) return end if not basic_parts then - ngx.log(ngx.ERR, "[basic-auth] header has unrecognized format") + kong.log.err("header has unrecognized format") return end @@ -58,7 +60,7 @@ local function retrieve_credentials(request, header_name, conf) end if conf.hide_credentials then - request.clear_header(header_name) + kong.service.request.clear_header(header_name) end return username, password @@ -71,8 +73,9 @@ end local function validate_credentials(credential, given_password) local digest, err = crypto.encrypt(credential.consumer.id, given_password) if err then - ngx.log(ngx.ERR, "[basic-auth] " .. err) + kong.log.err(err) end + return credential.password == digest end @@ -94,8 +97,10 @@ local function load_credential_from_db(username) load_credential_into_memory, username) if err then - return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) + kong.log.err(err) + return kong.response.exit(500, { message = "An unexpected error occurred" }) end + return credential end @@ -111,42 +116,54 @@ local function load_consumer_into_memory(consumer_id, anonymous) end local function set_consumer(consumer, credential) - 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 + kong.service.request.set_header(constants.HEADERS.CONSUMER_ID, consumer.id) + kong.service.request.set_header(constants.HEADERS.CONSUMER_CUSTOM_ID, consumer.custom_id) + kong.service.request.set_header(constants.HEADERS.CONSUMER_USERNAME, consumer.username) + + local shared_ctx = kong.ctx.shared + local ngx_ctx = ngx.ctx -- TODO: for bc only + + shared_ctx.authenticated_consumer = consumer + ngx_ctx.authenticated_consumer = consumer + if credential then - ngx_set_header(constants.HEADERS.CREDENTIAL_USERNAME, credential.username) - ngx.ctx.authenticated_credential = credential - ngx_set_header(constants.HEADERS.ANONYMOUS, nil) -- in case of auth plugins concatenation + shared_ctx.authenticated_credential = credential + ngx_ctx.authenticated_credential = credential + + kong.service.request.set_header(constants.HEADERS.CREDENTIAL_USERNAME, credential.username) + kong.service.request.clear_header(constants.HEADERS.ANONYMOUS) + else - ngx_set_header(constants.HEADERS.ANONYMOUS, true) + kong.service.request.set_header(constants.HEADERS.ANONYMOUS, true) end - end local function do_authentication(conf) -- If both headers are missing, return 401 - local headers = ngx_get_headers() - if not (headers["authorization"] or headers["proxy-authorization"]) then - ngx.header["WWW-Authenticate"] = realm - return false, {status = 401} + if not (kong.request.get_header("authorization") or kong.request.get_header("proxy-authorization")) then + return false, { + status = 401, + message = "Unauthorized", + headers = { + ["WWW-Authenticate"] = realm + } + } end local credential - local given_username, given_password = retrieve_credentials(ngx.req, "proxy-authorization", conf) + local given_username, given_password = retrieve_credentials("proxy-authorization", conf) if given_username then credential = load_credential_from_db(given_username) end -- Try with the authorization header if not credential then - given_username, given_password = retrieve_credentials(ngx.req, "authorization", conf) + given_username, given_password = retrieve_credentials("authorization", conf) credential = load_credential_from_db(given_username) end if not credential or not validate_credentials(credential, given_password) then - return false, {status = 403, message = "Invalid authentication credentials"} + return false, { status = 403, message = "Invalid authentication credentials" } end -- Retrieve consumer @@ -155,7 +172,8 @@ local function do_authentication(conf) load_consumer_into_memory, credential.consumer.id) if err then - return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) + kong.log.err(err) + return kong.response.exit(500, { message = "An unexpected error occurred" }) end set_consumer(consumer, credential) @@ -165,11 +183,20 @@ end function _M.execute(conf) + if conf.anonymous then + local shared_ctx = kong.ctx.shared + if shared_ctx.authenticated_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 + end - if ngx.ctx.authenticated_credential and conf.anonymous 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 + local ngx_ctx = ngx.ctx -- TODO: for bc only + if ngx_ctx.authenticated_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 + end end local ok, err = do_authentication(conf) @@ -181,11 +208,14 @@ function _M.execute(conf) load_consumer_into_memory, conf.anonymous, true) if err then - return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) + kong.log.err(err) + return kong.response.exit(500, { message = "An unexpected error occurred" }) end + set_consumer(consumer, nil) + else - return responses.send(err.status, err.message) + return kong.response.exit(err.status, { message = err.message }, err.headers) end end end diff --git a/kong/plugins/basic-auth/api.lua b/kong/plugins/basic-auth/api.lua index b9c751fc22e8..e822066ff58b 100644 --- a/kong/plugins/basic-auth/api.lua +++ b/kong/plugins/basic-auth/api.lua @@ -1,10 +1,11 @@ local endpoints = require "kong.api.endpoints" -local responses = require "kong.tools.responses" +local kong = kong local credentials_schema = kong.db.basicauth_credentials.schema local consumers_schema = kong.db.consumers.schema + return { ["/consumers/:consumers/basic-auth"] = { schema = credentials_schema, @@ -19,13 +20,13 @@ return { ["/consumers/:consumers/basic-auth/:basicauth_credentials"] = { schema = credentials_schema, methods = { - before = function(self, db, helpers) + before = function(self, db) local consumer, _, err_t = endpoints.select_entity(self, db, consumers_schema) if err_t then return endpoints.handle_error(err_t) end if not consumer then - return responses.send_HTTP_NOT_FOUND() + return kong.response.exit(404, { message = "Not found" }) end self.consumer = consumer @@ -37,17 +38,18 @@ return { if self.req.cmd_mth ~= "PUT" then if not cred or cred.consumer.id ~= consumer.id then - return responses.send_HTTP_NOT_FOUND() + return kong.response.exit(404, { message = "Not found" }) end + self.basicauth_credential = cred self.params.basicauth_credentials = cred.id end end, GET = endpoints.get_entity_endpoint(credentials_schema), - PUT = function(self, db, helpers) + PUT = function(self, ...) self.args.post.consumer = { id = self.consumer.id } - return endpoints.put_entity_endpoint(credentials_schema)(self, db, helpers) + return endpoints.put_entity_endpoint(credentials_schema)(self, ...) end, PATCH = endpoints.patch_entity_endpoint(credentials_schema), DELETE = endpoints.delete_entity_endpoint(credentials_schema), diff --git a/kong/plugins/basic-auth/basicauth_credentials.lua b/kong/plugins/basic-auth/basicauth_credentials.lua index b0b19adbac81..fc3d5a376298 100644 --- a/kong/plugins/basic-auth/basicauth_credentials.lua +++ b/kong/plugins/basic-auth/basicauth_credentials.lua @@ -1,5 +1,5 @@ local crypto = require "kong.plugins.basic-auth.crypto" -local utils = require "kong.tools.utils" +local utils = require "kong.tools.utils" local encrypt_password = function(self, cred_id_or_username, cred) diff --git a/kong/plugins/basic-auth/crypto.lua b/kong/plugins/basic-auth/crypto.lua index 6043594d3e12..073bac68f688 100644 --- a/kong/plugins/basic-auth/crypto.lua +++ b/kong/plugins/basic-auth/crypto.lua @@ -1,8 +1,8 @@ -- Module to encrypt the basic-auth credentials password field - -local resty_sha1 = require "resty.sha1" +local sha1 = require "resty.sha1" local to_hex = require "resty.string".to_hex -local format = string.format +local assert = assert + --- Salt the password -- Password is salted with the credential's consumer_id (long enough, unique) @@ -11,16 +11,18 @@ local function salt_password(consumer_id, password) if password == nil or password == ngx.null then password = "" end - return format("%s%s", password, consumer_id) + + return password .. consumer_id end + return { --- Encrypt the password field credential table -- @param credential The basic auth credential table -- @return hash of the salted credential's password encrypt = function(consumer_id, password) local salted = salt_password(consumer_id, password) - local digest = resty_sha1:new() + local digest = sha1:new() assert(digest:update(salted)) return to_hex(digest:final()) end diff --git a/kong/plugins/basic-auth/daos.lua b/kong/plugins/basic-auth/daos.lua index 48559812c5c4..dfb687f2efcf 100644 --- a/kong/plugins/basic-auth/daos.lua +++ b/kong/plugins/basic-auth/daos.lua @@ -1,5 +1,6 @@ local typedefs = require "kong.db.schema.typedefs" + return { basicauth_credentials = { dao = "kong.plugins.basic-auth.basicauth_credentials", diff --git a/kong/plugins/basic-auth/handler.lua b/kong/plugins/basic-auth/handler.lua index 92a8c5a0905d..5cb77318a5ed 100644 --- a/kong/plugins/basic-auth/handler.lua +++ b/kong/plugins/basic-auth/handler.lua @@ -1,20 +1,24 @@ -- Copyright (C) Kong Inc. - local BasePlugin = require "kong.plugins.base_plugin" local access = require "kong.plugins.basic-auth.access" + local BasicAuthHandler = BasePlugin:extend() + function BasicAuthHandler:new() BasicAuthHandler.super.new(self, "basic-auth") end + function BasicAuthHandler:access(conf) BasicAuthHandler.super.access(self) access.execute(conf) end + BasicAuthHandler.PRIORITY = 1001 -BasicAuthHandler.VERSION = "0.1.0" +BasicAuthHandler.VERSION = "0.2.0" + return BasicAuthHandler diff --git a/kong/plugins/basic-auth/schema.lua b/kong/plugins/basic-auth/schema.lua index c685231565b2..7aae3dd641b3 100644 --- a/kong/plugins/basic-auth/schema.lua +++ b/kong/plugins/basic-auth/schema.lua @@ -1,5 +1,6 @@ local typedefs = require "kong.db.schema.typedefs" + return { name = "basic-auth", fields = {