diff --git a/CHANGELOG.md b/CHANGELOG.md index bb4e3ccfb42..91cfc6b8626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,9 @@ - Relax multipart MIME type parsing. A space is allowed in between values of the Content-Type header. [#2215](https://github.com/Mashape/kong/pull/2215) +- Multiple auth plugins would overwrite eachothers results, causing + false negatives in an OR scenario. + [#2222](https://github.com/Mashape/kong/pull/2222) - Admin API: - Better handling of non-supported HTTP methods on endpoints of the Admin API. In some cases this used to throw an internal error. Calling any diff --git a/kong/plugins/basic-auth/access.lua b/kong/plugins/basic-auth/access.lua index b6c2d26821a..ed124f5eaed 100644 --- a/kong/plugins/basic-auth/access.lua +++ b/kong/plugins/basic-auth/access.lua @@ -147,7 +147,15 @@ local function do_authentication(conf) return true end + function _M.execute(conf) + + 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 + end + local ok, err = do_authentication(conf) if not ok then if conf.anonymous ~= "" then @@ -164,4 +172,5 @@ function _M.execute(conf) end end + return _M diff --git a/kong/plugins/hmac-auth/access.lua b/kong/plugins/hmac-auth/access.lua index ed2c82b4efc..d0fb879700e 100644 --- a/kong/plugins/hmac-auth/access.lua +++ b/kong/plugins/hmac-auth/access.lua @@ -216,7 +216,15 @@ local function do_authentication(conf) return true end + function _M.execute(conf) + + 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 + end + local ok, err = do_authentication(conf) if not ok then if conf.anonymous ~= "" then @@ -233,4 +241,5 @@ function _M.execute(conf) end end + return _M diff --git a/kong/plugins/jwt/handler.lua b/kong/plugins/jwt/handler.lua index 443fd76d8a3..3d6aee592b2 100644 --- a/kong/plugins/jwt/handler.lua +++ b/kong/plugins/jwt/handler.lua @@ -168,9 +168,16 @@ local function do_authentication(conf) return true end + function JwtHandler:access(conf) JwtHandler.super.access(self) + 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 + end + local ok, err = do_authentication(conf) if not ok then if conf.anonymous ~= "" then @@ -187,4 +194,5 @@ function JwtHandler:access(conf) end end + return JwtHandler diff --git a/kong/plugins/key-auth/handler.lua b/kong/plugins/key-auth/handler.lua index a8469d1d493..f0632ae9dbe 100644 --- a/kong/plugins/key-auth/handler.lua +++ b/kong/plugins/key-auth/handler.lua @@ -125,9 +125,16 @@ local function do_authentication(conf) return true end + function KeyAuthHandler:access(conf) KeyAuthHandler.super.access(self) + 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 + end + local ok, err = do_authentication(conf) if not ok then if conf.anonymous ~= "" then @@ -144,4 +151,5 @@ function KeyAuthHandler:access(conf) end end + return KeyAuthHandler diff --git a/kong/plugins/ldap-auth/access.lua b/kong/plugins/ldap-auth/access.lua index e188a20329d..5956c581483 100644 --- a/kong/plugins/ldap-auth/access.lua +++ b/kong/plugins/ldap-auth/access.lua @@ -102,20 +102,28 @@ local function load_consumer(consumer_id, anonymous) end local function set_consumer(consumer, credential) + if consumer then + -- this can only be the Anonymous user in this case 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) - end - 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 - else ngx_set_header(constants.HEADERS.ANONYMOUS, true) + ngx.ctx.authenticated_consumer = consumer + return end + -- here we have been authenticated by ldap + ngx_set_header(constants.HEADERS.CREDENTIAL_USERNAME, credential.username) + ngx.ctx.authenticated_credential = credential + + -- in case of auth plugins concatenation, remove remnants of anonymous + ngx.ctx.authenticated_consumer = nil + ngx_set_header(constants.HEADERS.ANONYMOUS, nil) + ngx_set_header(constants.HEADERS.CONSUMER_ID, nil) + ngx_set_header(constants.HEADERS.CONSUMER_CUSTOM_ID, nil) + ngx_set_header(constants.HEADERS.CONSUMER_USERNAME, nil) + end local function do_authentication(conf) @@ -148,7 +156,15 @@ local function do_authentication(conf) return true end + function _M.execute(conf) + + 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 + end + local ok, err = do_authentication(conf) if not ok then if conf.anonymous ~= "" then @@ -165,4 +181,5 @@ function _M.execute(conf) end end + return _M diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index a00933946b6..746fd56e26b 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -525,8 +525,17 @@ local function do_authentication(conf) return true end + function _M.execute(conf) + + 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 + end + if ngx.req.get_method() == "POST" then + local from, _, err = ngx.re.find(ngx.var.uri, [[\/oauth2\/token]], "oj") if err then ngx.log(ngx.ERR, "could not search for token path segment: ", err) @@ -559,10 +568,12 @@ function _M.execute(conf) return responses.send_HTTP_INTERNAL_SERVER_ERROR(err) end set_consumer(consumer, nil, nil) + else return responses.send(err.status, err.message, err.headers) end end end + return _M diff --git a/spec/03-plugins/10-key-auth/02-access_spec.lua b/spec/03-plugins/10-key-auth/02-access_spec.lua index 9265e1ccd14..f25562625f3 100644 --- a/spec/03-plugins/10-key-auth/02-access_spec.lua +++ b/spec/03-plugins/10-key-auth/02-access_spec.lua @@ -244,3 +244,198 @@ describe("Plugin: key-auth (access)", function() end) end) end) + + +describe("Plugin: key-auth (access)", function() + + local client, user1, user2, anonymous + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "basic-auth", + api_id = api1.id + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + user2 = assert(helpers.dao.consumers:insert { + username = "Aladdin" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "basic-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + assert(helpers.dao.basicauth_credentials:insert { + username = "Aladdin", + password = "OpenSesame", + consumer_id = user2.id + }) + + assert(helpers.start_kong()) + client = helpers.proxy_client() + end) + + + teardown(function() + if client then 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(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(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(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(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(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(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(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(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) diff --git a/spec/03-plugins/11-basic-auth/03-access_spec.lua b/spec/03-plugins/11-basic-auth/03-access_spec.lua index fd2202d05f7..687650f7845 100644 --- a/spec/03-plugins/11-basic-auth/03-access_spec.lua +++ b/spec/03-plugins/11-basic-auth/03-access_spec.lua @@ -4,7 +4,9 @@ local meta = require "kong.meta" local utils = require "kong.tools.utils" describe("Plugin: basic-auth (access)", function() + local client + setup(function() local api1 = assert(helpers.dao.apis:insert { name = "api-1", @@ -75,12 +77,16 @@ describe("Plugin: basic-auth (access)", function() assert(helpers.start_kong()) client = helpers.proxy_client() end) + + teardown(function() if client then client:close() end helpers.stop_kong() end) + describe("Unauthorized", function() + it("returns Unauthorized on missing credentials", function() local res = assert(client:send { method = "GET", @@ -92,6 +98,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(401, res) assert.equal([[{"message":"Unauthorized"}]], body) end) + it("returns WWW-Authenticate header on missing credentials", function() local res = assert(client:send { method = "GET", @@ -103,9 +110,12 @@ describe("Plugin: basic-auth (access)", function() assert.res_status(401, res) assert.equal('Basic realm="'..meta._NAME..'"', res.headers["WWW-Authenticate"]) end) + end) + describe("Forbidden", function() + it("returns 403 Forbidden on invalid credentials in Authorization", function() local res = assert(client:send { method = "GET", @@ -118,6 +128,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(403, res) assert.equal([[{"message":"Invalid authentication credentials"}]], body) end) + it("returns 403 Forbidden on invalid credentials in Proxy-Authorization", function() local res = assert(client:send { method = "GET", @@ -130,6 +141,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(403, res) assert.equal([[{"message":"Invalid authentication credentials"}]], body) end) + it("returns 403 Forbidden on password only", function() local res = assert(client:send { method = "GET", @@ -142,6 +154,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(403, res) assert.equal([[{"message":"Invalid authentication credentials"}]], body) end) + it("returns 403 Forbidden on username only", function() local res = assert(client:send { method = "GET", @@ -154,6 +167,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(403, res) assert.equal([[{"message":"Invalid authentication credentials"}]], body) end) + it("authenticates valid credentials in Authorization", function() local res = assert(client:send { method = "GET", @@ -165,6 +179,7 @@ describe("Plugin: basic-auth (access)", function() }) assert.res_status(200, res) end) + it("authenticates valid credentials in Authorization", function() local res = assert(client:send { method = "GET", @@ -177,6 +192,7 @@ describe("Plugin: basic-auth (access)", function() local body = cjson.decode(assert.res_status(200, res)) assert.equal('bob', body.headers["x-consumer-username"]) end) + it("returns 403 for valid Base64 encoding", function() local res = assert(client:send { method = "GET", @@ -189,6 +205,7 @@ describe("Plugin: basic-auth (access)", function() local body = assert.res_status(403, res) assert.equal([[{"message":"Invalid authentication credentials"}]], body) end) + it("authenticates valid credentials in Proxy-Authorization", function() local res = assert(client:send { method = "GET", @@ -200,9 +217,12 @@ describe("Plugin: basic-auth (access)", function() }) assert.res_status(200, res) end) + end) + describe("Consumer headers", function() + it("sends Consumer headers to upstream", function() local res = assert(client:send { method = "GET", @@ -217,9 +237,12 @@ describe("Plugin: basic-auth (access)", function() assert.is_string(json.headers["x-consumer-id"]) assert.equal("bob", json.headers["x-consumer-username"]) end) + end) + describe("config.hide_credentials", function() + it("false sends key to upstream", function() local res = assert(client:send { method = "GET", @@ -233,6 +256,7 @@ describe("Plugin: basic-auth (access)", function() 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(client:send { method = "GET", @@ -246,9 +270,12 @@ describe("Plugin: basic-auth (access)", function() 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(client:send { method = "GET", @@ -262,6 +289,7 @@ describe("Plugin: basic-auth (access)", function() assert.equal('bob', body.headers["x-consumer-username"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) + it("works with wrong credentials and anonymous", function() local res = assert(client:send { method = "GET", @@ -274,6 +302,7 @@ describe("Plugin: basic-auth (access)", function() 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(client:send { method = "GET", @@ -284,5 +313,201 @@ describe("Plugin: basic-auth (access)", function() }) assert.response(res).has.status(500) end) + + end) + +end) + +describe("Plugin: basic-auth (access)", function() + + local client, user1, user2, anonymous + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "basic-auth", + api_id = api1.id + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + user2 = assert(helpers.dao.consumers:insert { + username = "Aladdin" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "basic-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + assert(helpers.dao.basicauth_credentials:insert { + username = "Aladdin", + password = "OpenSesame", + consumer_id = user2.id + }) + + assert(helpers.start_kong()) + client = helpers.proxy_client() end) + + + teardown(function() + if client then 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(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(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(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(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(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(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(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(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) diff --git a/spec/03-plugins/17-jwt/03-access_spec.lua b/spec/03-plugins/17-jwt/03-access_spec.lua index 9e56becbd85..31194093713 100644 --- a/spec/03-plugins/17-jwt/03-access_spec.lua +++ b/spec/03-plugins/17-jwt/03-access_spec.lua @@ -335,3 +335,199 @@ describe("Plugin: jwt (access)", function() end) end) end) + + +describe("Plugin: jwt (access)", function() + + local client, user1, user2, anonymous, jwt_token + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "jwt", + api_id = api1.id + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + user2 = assert(helpers.dao.consumers:insert { + username = "Aladdin" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "jwt", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + + local jwt_secret = assert(helpers.dao.jwt_secrets:insert { + consumer_id = user2.id + }) + PAYLOAD.iss = jwt_secret.key + jwt_token = "Bearer "..jwt_encoder.encode(PAYLOAD, jwt_secret.secret) + + assert(helpers.start_kong()) + client = helpers.proxy_client() + end) + + + teardown(function() + if client then 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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["apikey"] = "Mouse", + ["Authorization"] = jwt_token, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["Authorization"] = jwt_token, + } + }) + assert.response(res).has.status(401) + end) + + it("fails 401, with no credential provided", function() + local res = assert(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["apikey"] = "Mouse", + ["Authorization"] = jwt_token, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["Authorization"] = jwt_token, + } + }) + 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(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) diff --git a/spec/03-plugins/20-hmac-auth/03-access_spec.lua b/spec/03-plugins/20-hmac-auth/03-access_spec.lua index ef64027705d..19510556683 100644 --- a/spec/03-plugins/20-hmac-auth/03-access_spec.lua +++ b/spec/03-plugins/20-hmac-auth/03-access_spec.lua @@ -241,7 +241,7 @@ describe("Plugin: hmac-auth (access)", function() it("should pass with GET", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64(hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "GET", @@ -261,7 +261,7 @@ describe("Plugin: hmac-auth (access)", function() it("should pass with GET and proxy-authorization", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64(hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "GET", @@ -279,7 +279,7 @@ describe("Plugin: hmac-auth (access)", function() it("should pass with POST", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64(hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "POST", @@ -299,7 +299,7 @@ describe("Plugin: hmac-auth (access)", function() it("should pass with GET and valid authorization and wrong proxy-authorization", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64(hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "POST", @@ -321,7 +321,7 @@ describe("Plugin: hmac-auth (access)", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "POST", @@ -341,7 +341,7 @@ describe("Plugin: hmac-auth (access)", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."content-md5: md5")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date content-md5",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "GET", @@ -363,7 +363,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " ..date.."\n".."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1", ]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] ..[[headers="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -386,7 +386,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bobb", algorithm="hmac-sha1", ]] + local hmacAuth = [[hmac username="bobb", algorithm="hmac-sha1", ]] ..[[headers="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -412,7 +412,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="", algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="", algorithm="hmac-sha1",]] ..[[ headers="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -439,7 +439,7 @@ describe("Plugin: hmac-auth (access)", function() hmac_sha1_binary("secret", "date: "..date.."\n".."content-md5: md5" .."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac algorithm="hmac-sha1", ]] + local hmacAuth = [[hmac algorithm="hmac-sha1", ]] ..[[headers="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -465,7 +465,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1", ]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] ..[[wrong_header="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -490,7 +490,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1",]] ..[[ headers="date content-md5 request-line", wrong_signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -515,7 +515,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1"]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1"]] ..[[ headers="date content-md5 request-line", signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -540,7 +540,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /request? HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1" ]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1" ]] ..[[headers=" date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -565,7 +565,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -588,7 +588,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha256",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha256",]] ..[[ headers="date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -611,7 +611,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha256",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha256",]] ..[[ headers="date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -639,7 +639,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "x-date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="x-date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -660,7 +660,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "content-md5: md5" .."\nGET /request? HTTP/1.1")) - local hmacAuth = [["hmac username="bob", algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1",]] ..[[ headers="content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -685,7 +685,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "x-date: "..date.."\n" .."content-md5: md5".."\nGET /request? HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="x-date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -710,7 +710,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "content-md5: md5" .."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -733,7 +733,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -756,7 +756,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "x-date: ".."wrong date".."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="x-date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -779,7 +779,7 @@ describe("Plugin: hmac-auth (access)", function() local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: ".."wrong date".."\n" .."content-md5: md5".."\nGET /requests HTTP/1.1")) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[ headers="date content-md5 request-line",signature="]] ..encodedSignature..[["]] local res = assert(client:send { @@ -800,7 +800,7 @@ describe("Plugin: hmac-auth (access)", function() it("should pass with valid credentials and anonymous", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64(hmac_sha1_binary("secret", "date: "..date)) - local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] + local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(client:send { method = "GET", @@ -845,3 +845,207 @@ describe("Plugin: hmac-auth (access)", function() end) end) end) + + + +describe("Plugin: hmac-auth (access)", function() + + local client, user1, user2, anonymous, hmacAuth, hmacDate + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "hmac-auth", + api_id = api1.id + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + user2 = assert(helpers.dao.consumers:insert { + username = "Aladdin" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "hmac-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + local credential = assert(helpers.dao.hmacauth_credentials:insert { + username = "Aladdin", + secret = "OpenSesame", + consumer_id = user2.id + }) + hmacDate = os.date("!%a, %d %b %Y %H:%M:%S GMT") + local encodedSignature = ngx.encode_base64(hmac_sha1_binary(credential.secret, "date: "..hmacDate)) + hmacAuth = [[hmac username="]]..credential.username..[[",algorithm="hmac-sha1",]] + ..[[headers="date",signature="]]..encodedSignature..[["]] + + assert(helpers.start_kong()) + client = helpers.proxy_client() + end) + + + teardown(function() + if client then 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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["apikey"] = "Mouse", + ["Authorization"] = hmacAuth, + ["date"] = hmacDate, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["Authorization"] = hmacAuth, + ["date"] = hmacDate, + } + }) + assert.response(res).has.status(401) + end) + + it("fails 401, with no credential provided", function() + local res = assert(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["apikey"] = "Mouse", + ["Authorization"] = hmacAuth, + ["date"] = hmacDate, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["Authorization"] = hmacAuth, + ["date"] = hmacDate, + } + }) + 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(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) diff --git a/spec/03-plugins/21-ldap-auth/01-access_spec.lua b/spec/03-plugins/21-ldap-auth/01-access_spec.lua index 311c0e5e07f..78142288192 100644 --- a/spec/03-plugins/21-ldap-auth/01-access_spec.lua +++ b/spec/03-plugins/21-ldap-auth/01-access_spec.lua @@ -2,9 +2,10 @@ local cache = require "kong.tools.database_cache" local helpers = require "spec.helpers" local utils = require "kong.tools.utils" +local ldap_host_aws = "ec2-54-172-82-117.compute-1.amazonaws.com" + describe("Plugin: ldap-auth (access)", function() local client, client_admin, api2, plugin2 - local ldap_host_aws = "ec2-54-172-82-117.compute-1.amazonaws.com" setup(function() local api1 = assert(helpers.dao.apis:insert { name = "test-ldap", @@ -336,3 +337,199 @@ describe("Plugin: ldap-auth (access)", function() end) end) end) + + + +describe("Plugin: ldap-auth (access)", function() + + local client, user1, user2, anonymous + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + api_id = api1.id, + name = "ldap-auth", + config = { + ldap_host = ldap_host_aws, + ldap_port = "389", + start_tls = false, + base_dn = "ou=scientists,dc=ldap,dc=mashape,dc=com", + attribute = "uid", + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + api_id = api2.id, + name = "ldap-auth", + config = { + ldap_host = ldap_host_aws, + ldap_port = "389", + start_tls = false, + base_dn = "ou=scientists,dc=ldap,dc=mashape,dc=com", + attribute = "uid", + anonymous = anonymous.id, + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + + assert(helpers.start_kong()) + client = helpers.proxy_client() + end) + + + teardown(function() + if client then 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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["apikey"] = "Mouse", + ["Authorization"] = "ldap "..ngx.encode_base64("einstein:password"), + } + }) + assert.response(res).has.status(200) + assert.request(res).has.no.header("x-anonymous-consumer") + end) + + it("fails 401, with only the first credential provided", function() + local res = assert(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["Authorization"] = "ldap "..ngx.encode_base64("einstein:password"), + } + }) + assert.response(res).has.status(401) + end) + + it("fails 401, with no credential provided", function() + local res = assert(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["apikey"] = "Mouse", + ["Authorization"] = "ldap "..ngx.encode_base64("einstein:password"), + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["Authorization"] = "ldap "..ngx.encode_base64("einstein:password"), + } + }) + assert.response(res).has.status(200) + assert.request(res).has.no.header("x-anonymous-consumer") + local id = assert.request(res).has.header("x-credential-username") + assert.equal("einstein", id) + end) + + it("passes with no credential provided", function() + local res = assert(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) diff --git a/spec/03-plugins/26-oauth2/03-access_spec.lua b/spec/03-plugins/26-oauth2/03-access_spec.lua index 50b4266d9af..19bc8330318 100644 --- a/spec/03-plugins/26-oauth2/03-access_spec.lua +++ b/spec/03-plugins/26-oauth2/03-access_spec.lua @@ -2,6 +2,56 @@ local cjson = require "cjson" local helpers = require "spec.helpers" local utils = require "kong.tools.utils" +local function provision_code(host, extra_headers) + local request_client = helpers.proxy_ssl_client() + local res = assert(request_client:send { + method = "POST", + path = "/oauth2/authorize", + body = { + provision_key = "provision123", + client_id = "clientid123", + scope = "email", + response_type = "code", + state = "hello", + authenticated_userid = "userid123" + }, + headers = utils.table_merge({ + ["Host"] = host or "oauth2.com", + ["Content-Type"] = "application/json" + }, extra_headers) + }) + assert.response(res).has.status(200) + local body = assert.response(res).has.jsonbody() + request_client:close() + if body.redirect_uri then + local iterator, err = ngx.re.gmatch(body.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") + assert.is_nil(err) + local m, err = iterator() + assert.is_nil(err) + return m[1] + end +end + +local function provision_token(host, extra_headers) + local code = provision_code(host, extra_headers) + local request_client = helpers.proxy_ssl_client() + local res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { code = code, client_id = "clientid123", client_secret = "secret123", grant_type = "authorization_code" }, + headers = utils.table_merge({ + ["Host"] = host or "oauth2.com", + ["Content-Type"] = "application/json" + }, extra_headers) + }) + assert.response(res).has.status(200) + local token = assert.response(res).has.jsonbody() + assert.is_table(token) + request_client:close() + return token +end + + describe("#ci Plugin: oauth2 (access)", function() local proxy_ssl_client, proxy_client local client1 @@ -252,55 +302,6 @@ describe("#ci Plugin: oauth2 (access)", function() helpers.stop_kong() end) - local function provision_code(host) - local request_client = helpers.proxy_ssl_client() - local res = assert(request_client:send { - method = "POST", - path = "/oauth2/authorize", - body = { - provision_key = "provision123", - client_id = "clientid123", - scope = "email", - response_type = "code", - state = "hello", - authenticated_userid = "userid123" - }, - headers = { - ["Host"] = host and host or "oauth2.com", - ["Content-Type"] = "application/json" - } - }) - assert.response(res).has.status(200) - local body = assert.response(res).has.jsonbody() - request_client:close() - if body.redirect_uri then - local iterator, err = ngx.re.gmatch(body.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") - assert.is_nil(err) - local m, err = iterator() - assert.is_nil(err) - return m[1] - end - end - - local function provision_token(host) - local code = provision_code(host) - local request_client = helpers.proxy_ssl_client() - local res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = { code = code, client_id = "clientid123", client_secret = "secret123", grant_type = "authorization_code" }, - headers = { - ["Host"] = host and host or "oauth2.com", - ["Content-Type"] = "application/json" - } - }) - assert.response(res).has.status(200) - local token = assert.response(res).has.jsonbody() - assert.is_table(token) - request_client:close() - return token - end - describe("OAuth2 Authorization", function() describe("Code Grant", function() it("returns an error when no provision_key is being sent", function() @@ -2045,3 +2046,225 @@ describe("#ci Plugin: oauth2 (access)", function() end) end) end) + + +describe("#ci Plugin: oauth2 (access)", function() + + local client, user1, user2, anonymous + + setup(function() + local api1 = assert(helpers.dao.apis:insert { + name = "api-1", + hosts = { "logical-and.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "oauth2", + api_id = api1.id, + config = { + scopes = { "email", "profile", "user.email" }, + enable_authorization_code = true, + mandatory_scope = true, + provision_key = "provision123", + token_expiration = 5, + enable_implicit_grant = true, + global_credentials = false, + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api1.id + }) + + anonymous = assert(helpers.dao.consumers:insert { + username = "Anonymous" + }) + user1 = assert(helpers.dao.consumers:insert { + username = "Mickey" + }) + user2 = assert(helpers.dao.consumers:insert { + username = "Aladdin" + }) + + local api2 = assert(helpers.dao.apis:insert { + name = "api-2", + hosts = { "logical-or.com" }, + upstream_url = "http://mockbin.org/request" + }) + assert(helpers.dao.plugins:insert { + name = "oauth2", + api_id = api2.id, + config = { + scopes = { "email", "profile", "user.email" }, + enable_authorization_code = true, + mandatory_scope = true, + provision_key = "provision123", + token_expiration = 5, + enable_implicit_grant = true, + global_credentials = false, + anonymous = anonymous.id, + } + }) + assert(helpers.dao.plugins:insert { + name = "key-auth", + api_id = api2.id, + config = { + anonymous = anonymous.id + } + }) + + assert(helpers.dao.keyauth_credentials:insert { + key = "Mouse", + consumer_id = user1.id + }) + + assert(helpers.dao.oauth2_credentials:insert { + client_id = "clientid123", + client_secret = "secret123", + redirect_uri = "http://google.com/kong", + name = "testapp", + consumer_id = user2.id + }) + + assert(helpers.start_kong()) + client = helpers.proxy_client() + end) + + + teardown(function() + if client then 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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + ["apikey"] = "Mouse", + -- we must provide the apikey again in the extra_headers, for the + -- token endpoint, because that endpoint is also protected by the + -- key-auth plugin. Otherwise getting the token simply fails. + ["Authorization"] = "bearer "..provision_token("logical-and.com", + {["apikey"] = "Mouse"}).access_token, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-and.com", + -- we must provide the apikey again in the extra_headers, for the + -- token endpoint, because that endpoint is also protected by the + -- key-auth plugin. Otherwise getting the token simply fails. + ["Authorization"] = "bearer "..provision_token("logical-and.com", + {["apikey"] = "Mouse"}).access_token, + } + }) + assert.response(res).has.status(401) + end) + + it("fails 401, with no credential provided", function() + local res = assert(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["apikey"] = "Mouse", + ["Authorization"] = "bearer "..provision_token("logical-or.com").access_token, + } + }) + 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(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(client:send { + method = "GET", + path = "/request", + headers = { + ["Host"] = "logical-or.com", + ["Authorization"] = "bearer "..provision_token("logical-or.com").access_token, + } + }) + 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(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)