kong/spec/03-plugins/10-basic-auth/03-access_spec.lua (555 lines of code) (raw):

local helpers = require "spec.helpers" local cjson = require "cjson" local meta = require "kong.meta" local utils = require "kong.tools.utils" for _, strategy in helpers.each_strategy() do describe("Plugin: basic-auth (access) [#" .. strategy .. "]", function() local proxy_client lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins", "consumers", "basicauth_credentials", }) local consumer = bp.consumers:insert { username = "bob", } local anonymous_user = bp.consumers:insert { username = "no-body", } local route1 = bp.routes:insert { hosts = { "basic-auth1.com" }, } local route2 = bp.routes:insert { hosts = { "basic-auth2.com" }, } local route3 = bp.routes:insert { hosts = { "basic-auth3.com" }, } local route4 = bp.routes:insert { hosts = { "basic-auth4.com" }, } local route5 = bp.routes:insert { hosts = { "basic-auth5.com" }, } local route_grpc = assert(bp.routes:insert { protocols = { "grpc" }, paths = { "/hello.HelloService/" }, service = assert(bp.services:insert { name = "grpc", url = helpers.grpcbin_url, }), }) bp.plugins:insert { name = "basic-auth", route = { id = route1.id }, } bp.plugins:insert { name = "basic-auth", route = { id = route_grpc.id }, } bp.plugins:insert { name = "basic-auth", route = { id = route2.id }, config = { hide_credentials = true, }, } bp.basicauth_credentials:insert { username = "bob", password = "kong", consumer = { id = consumer.id }, } bp.basicauth_credentials:insert { username = "user123", password = "password123", consumer = { id = consumer.id }, } bp.basicauth_credentials:insert { username = "user321", password = "password:123", consumer = { id = consumer.id }, } bp.plugins:insert { name = "basic-auth", route = { id = route3.id }, config = { anonymous = anonymous_user.id, }, } bp.plugins:insert { name = "basic-auth", route = { id = route4.id }, config = { anonymous = utils.uuid(), -- a non-existing consumer id }, } bp.plugins:insert { name = "basic-auth", route = { id = route5.id }, config = { anonymous = anonymous_user.username, }, } assert(helpers.start_kong({ database = strategy, nginx_conf = "spec/fixtures/custom_nginx.template", })) proxy_client = helpers.proxy_client() end) lazy_teardown(function() if proxy_client then proxy_client:close() end helpers.stop_kong() end) describe("Unauthorized", function() it("returns Unauthorized on missing credentials", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Unauthorized" }, json) end) it("returns WWW-Authenticate header on missing credentials", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "basic-auth1.com" } }) assert.res_status(401, res) assert.equal('Basic realm="' .. meta._NAME .. '"', res.headers["WWW-Authenticate"]) end) end) describe("Unauthorized", function() it("returns 401 Unauthorized on invalid credentials in Authorization", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Authorization"] = "foobar", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid authentication credentials" }, json) end) it("returns 401 Unauthorized on invalid credentials in Proxy-Authorization", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Proxy-Authorization"] = "foobar", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid authentication credentials" }, json) end) it("returns 401 Unauthorized on password only", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Authorization"] = "Basic a29uZw==", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid authentication credentials" }, json) end) it("returns 401 Unauthorized on username only", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Authorization"] = "Basic Ym9i", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid authentication credentials" }, json) end) it("rejects gRPC call without credentials", function() local ok, err = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = {}, } assert.falsy(ok) assert.matches("Code: Unauthenticated", err) end) it("accepts authorized gRPC calls", function() local ok, res = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-H"] = "'Authorization: Basic Ym9iOmtvbmc='", }, } assert.truthy(ok) assert.same({ reply = "hello noname" }, cjson.decode(res)) end) it("authenticates valid credentials in Authorization", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Authorization"] = "Basic Ym9iOmtvbmc=", ["Host"] = "basic-auth1.com" } }) assert.res_status(200, res) end) it("authenticates valid credentials in Authorization", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic dXNlcjEyMzpwYXNzd29yZDEyMw==", ["Host"] = "basic-auth1.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('bob', body.headers["x-consumer-username"]) assert.equal('user123', body.headers["x-credential-identifier"]) end) it("authenticates with a password containing ':'", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic dXNlcjMyMTpwYXNzd29yZDoxMjM=", ["Host"] = "basic-auth1.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal("bob", body.headers["x-consumer-username"]) assert.equal("user321", body.headers["x-credential-identifier"]) end) it("returns 401 for valid Base64 encoding", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Authorization"] = "Basic adXNlcjEyMzpwYXNzd29yZDEyMw==", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid authentication credentials" }, json) end) it("authenticates valid credentials in Proxy-Authorization", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Proxy-Authorization"] = "Basic Ym9iOmtvbmc=", ["Host"] = "basic-auth1.com" } }) assert.res_status(200, res) end) end) describe("Consumer headers", function() it("sends Consumer headers to upstream", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic Ym9iOmtvbmc=", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_string(json.headers["x-consumer-id"]) assert.equal("bob", json.headers["x-consumer-username"]) assert.equal("bob", json.headers["x-credential-identifier"]) end) end) describe("config.hide_credentials", function() it("false sends key to upstream", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic Ym9iOmtvbmc=", ["Host"] = "basic-auth1.com" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("Basic Ym9iOmtvbmc=", json.headers.authorization) end) it("true doesn't send key to upstream", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic Ym9iOmtvbmc=", ["Host"] = "basic-auth2.com" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.is_nil(json.headers.authorization) end) end) describe("config.anonymous", function() it("works with right credentials and anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = "Basic dXNlcjEyMzpwYXNzd29yZDEyMw==", ["Host"] = "basic-auth3.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('bob', body.headers["x-consumer-username"]) assert.equal('user123', body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) it("works with wrong credentials and anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "basic-auth3.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('true', body.headers["x-anonymous-consumer"]) assert.equal('no-body', body.headers["x-consumer-username"]) assert.equal(nil, body.headers["x-credential-identifier"]) end) it("works with wrong credentials and username in anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "basic-auth5.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('true', body.headers["x-anonymous-consumer"]) assert.equal('no-body', body.headers["x-consumer-username"]) end) it("errors when anonymous user doesn't exist", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "basic-auth4.com" } }) assert.response(res).has.status(500) end) end) end) describe("Plugin: basic-auth (access) [#" .. strategy .. "]", function() local proxy_client local user1 local user2 local anonymous lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins", "consumers", "basicauth_credentials", "keyauth_credentials", }) anonymous = bp.consumers:insert { username = "Anonymous", } user1 = bp.consumers:insert { username = "Mickey", } user2 = bp.consumers:insert { username = "Aladdin", } local service1 = bp.services:insert { path = "/request", } local service2 = bp.services:insert { path = "/request", } local route1 = bp.routes:insert { hosts = { "logical-and.com" }, service = service1, } local route2 = bp.routes:insert { hosts = { "logical-or.com" }, service = service2, } bp.plugins:insert { name = "basic-auth", route = { id = route1.id }, } bp.plugins:insert { name = "key-auth", route = { id = route1.id }, } bp.plugins:insert { name = "basic-auth", route = { id = route2.id }, config = { anonymous = anonymous.id, }, } bp.plugins:insert { name = "key-auth", route = { id = route2.id }, config = { anonymous = anonymous.id, }, } bp.keyauth_credentials:insert({ key = "Mouse", consumer = { id = user1.id }, }) bp.basicauth_credentials:insert { username = "Aladdin", password = "OpenSesame", consumer = { id = user2.id }, } assert(helpers.start_kong({ database = strategy, nginx_conf = "spec/fixtures/custom_nginx.template", })) proxy_client = helpers.proxy_client() end) lazy_teardown(function() if proxy_client then proxy_client:close() end helpers.stop_kong() end) describe("multiple auth without anonymous, logical AND", function() it("passes with all credentials provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["apikey"] = "Mouse", ["Authorization"] = "Basic QWxhZGRpbjpPcGVuU2VzYW1l", } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(id, anonymous.id) assert(id == user1.id or id == user2.id) end) it("fails 401, with only the first credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["apikey"] = "Mouse", } }) assert.response(res).has.status(401) end) it("fails 401, with only the second credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["Authorization"] = "Basic QWxhZGRpbjpPcGVuU2VzYW1l", } }) assert.response(res).has.status(401) end) it("fails 401, with no credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", } }) assert.response(res).has.status(401) end) end) describe("multiple auth with anonymous, logical OR", function() it("passes with all credentials provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["apikey"] = "Mouse", ["Authorization"] = "Basic QWxhZGRpbjpPcGVuU2VzYW1l", } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(id, anonymous.id) assert(id == user1.id or id == user2.id) end) it("passes with only the first credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["apikey"] = "Mouse", } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(id, anonymous.id) assert.equal(user1.id, id) end) it("passes with only the second credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["Authorization"] = "Basic QWxhZGRpbjpPcGVuU2VzYW1l", } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(id, anonymous.id) assert.equal(user2.id, id) end) it("passes with no credential provided", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", } }) assert.response(res).has.status(200) assert.request(res).has.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.equal(id, anonymous.id) end) end) end) end