kong/spec/03-plugins/19-hmac-auth/03-access_spec.lua (1,780 lines of code) (raw):

local cjson = require "cjson" local openssl_hmac = require "resty.openssl.hmac" local helpers = require "spec.helpers" local utils = require "kong.tools.utils" local resty_sha256 = require "resty.sha256" local fmt = string.format local hmac_sha1_binary = function(secret, data) return openssl_hmac.new(secret, "sha1"):final(data) end local SIGNATURE_NOT_VALID = "HMAC signature cannot be verified" for _, strategy in helpers.each_strategy() do describe("Plugin: hmac-auth (access) [#" .. strategy .. "]", function() local proxy_client local consumer local credential lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "consumers", "plugins", "hmacauth_credentials", }) local route1 = bp.routes:insert { hosts = { "hmacauth.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 = "hmac-auth", route = { id = route1.id }, config = { clock_skew = 3000 } } bp.plugins:insert { name = "hmac-auth", route = { id = route_grpc.id }, config = { clock_skew = 3000 } } consumer = bp.consumers:insert { username = "bob", custom_id = "1234" } credential = bp.hmacauth_credentials:insert { username = "bob", secret = "secret", consumer = { id = consumer.id }, } local anonymous_user = bp.consumers:insert { username = "no-body" } local route2 = bp.routes:insert { hosts = { "hmacauth2.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route2.id }, config = { anonymous = anonymous_user.id, clock_skew = 3000 } } local route3 = bp.routes:insert { hosts = { "hmacauth3.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route3.id }, config = { anonymous = utils.uuid(), -- non existing consumer clock_skew = 3000 } } local route4 = bp.routes:insert { hosts = { "hmacauth4.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route4.id }, config = { clock_skew = 3000, validate_request_body = true } } local route5 = bp.routes:insert { hosts = { "hmacauth5.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route5.id }, config = { clock_skew = 3000, enforce_headers = {"date", "request-line"}, validate_request_body = true } } local route6 = bp.routes:insert { hosts = { "hmacauth6.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route6.id }, config = { clock_skew = 3000, enforce_headers = {"date", "request-line"}, algorithms = {"hmac-sha1", "hmac-sha256"}, validate_request_body = true } } local route7 = bp.routes:insert { hosts = { "hmacauth7.com" }, } bp.plugins:insert { name = "hmac-auth", route = { id = route7.id }, config = { anonymous = anonymous_user.username, clock_skew = 3000 } } assert(helpers.start_kong { database = strategy, real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "0.0.0.0/0, ::/0", 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("HMAC Authentication", function() it("should not be authorized when the hmac credentials are missing", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("Unauthorized", body.message) 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("should not be authorized when the HMAC signature is wrong", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = "asd" } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not be authorized when the HMAC signature is not properly base64 encoded", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] .. [[headers="date",signature="not really a base64 encoded value!!!"]] local res = assert(proxy_client:send { method = "POST", headers = { ["HOST"] = "hmacauth.com", date = date, authorization = hmacAuth } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("HMAC signature does not match", body.message) end) it("should not be authorized when date header is missing", function() local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", authorization = "asd" } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal([[HMAC signature cannot be verified, ]] .. [[a valid date or x-date header is]] .. [[ required for HMAC Authentication]], body.message) end) it("should not be authorized with signature is wrong in proxy-authorization", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = "asd" } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass when passing only the digest", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = "hmac :dXNlcm5hbWU6cGFzc3dvcmQ=" } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass when passing wrong hmac parameters", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = [[hmac username=,algorithm,]] .. [[headers,dXNlcm5hbWU6cGFzc3dvcmQ=]] } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass when passing wrong hmac parameters", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = [[hmac username=,algorithm,]] .. [[headers,dXNlcm5hbWU6cGFzc3dvcmQ=]] } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not be authorized when passing only the username", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = "hmac username" } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not be authorized when authorization header is missing", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "POST", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("Unauthorized", body.message) end) it("should not pass with username missing", 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 algorithm="hmac-sha1",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.same({ message = "HMAC signature cannot be verified" }, body) end) it("should not pass with signature missing", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[headers="date"]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.same({ message = "HMAC signature cannot be verified" }, body) end) 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(200, res) body = cjson.decode(body) assert.equal(hmacAuth, body.headers["authorization"]) end) it("accepts authorized gRPC calls", 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local ok, res = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { [""] = ("-H 'Date: %s' -H 'Authorization: %s'"):format(date, hmacAuth), }, } assert.truthy(ok) assert.same({ reply = "hello noname" }, cjson.decode(res)) end) it("accepts authorized gRPC calls with @request-target (HTTP/2 test), bug #3789", 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@request-target: " .. "post /hello.HelloService/SayHello")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[headers="date @request-target",signature="]] .. encodedSignature .. [["]] local ok, res = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { [""] = ("-H 'Date: %s' -H 'Authorization: %s'"):format(date, hmacAuth), }, } assert.truthy(ok) assert.same({ reply = "hello noname" }, cjson.decode(res)) end) 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, }, }) assert.res_status(200, res) end) 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(200, res) body = cjson.decode(body) assert.equal(hmacAuth, body.headers["authorization"]) end) 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = "hmac username", authorization = hmacAuth, }, }) local body = assert.res_status(200, res) body = cjson.decode(body) assert.equal(hmacAuth, body.headers["authorization"]) end) it("should pass with GET and invalid authorization and valid 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", }, }) assert.res_status(200, res) end) it("should pass with GET with content-md5 header", 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",]] .. [[headers="date content-md5",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with GET with request-line", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with GET with @request-target", 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" .. "\n@request-target: get /request")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 @request-target", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should encode http-1 requests as http/1.0", 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" .. "\nGET /request HTTP/1.0")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { version = 1.0, method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should not pass with GET with wrong username in signature", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bobb", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with username blank in signature", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="", algorithm="hmac-sha1",]] .. [[ headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with username missing in signature", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with wrong hmac headers field name", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[wrong_header="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with wrong hmac signature field name", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1",]] .. [[ headers="date content-md5 request-line", wrong_signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with malformed hmac signature field", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1"]] .. [[ headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should not pass with GET with malformed hmac headers field", 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" .. "\nGET /request? HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1" ]] .. [[headers=" date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal(SIGNATURE_NOT_VALID, body.message) end) it("should pass with GET with no space or space between hmac signatures fields", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[ headers="date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should not pass with GET with wrong algorithm", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha256"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha",]] .. [[ headers="date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) assert.res_status(401, res) end) it("should pass the right headers to the upstream server", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha256"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha256",]] .. [[ headers="date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", date = date, ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local parsed_body = cjson.decode(body) assert.equal(consumer.id, parsed_body.headers["x-consumer-id"]) assert.equal(consumer.username, parsed_body.headers["x-consumer-username"]) assert.equal(credential.username, parsed_body.headers["x-credential-identifier"]) assert.is_nil(parsed_body.headers["x-anonymous-consumer"]) end) it("should pass with GET with x-date header", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") 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",]] .. [[ headers="x-date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = date, authorization = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should not pass with GET with both date and x-date missing", 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",]] .. [[ headers="content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["proxy-authorization"] = hmacAuth, authorization = "hello", ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal([[HMAC signature cannot be verified, a valid date or]] .. [[ x-date header is required for HMAC Authentication]], body.message) end) it("should not pass with GET with x-date malformed", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") 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",]] .. [[ headers="x-date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = "wrong date", authorization = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal([[HMAC signature cannot be verified, a valid date or]] .. [[ x-date header is required for HMAC Authentication]], body.message) end) it("should pass with GET with x-date malformed but date correct", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") 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",]] .. [[ headers="content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = "wrong date", date = date, authorization = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with x-date malformed but date correct and used for signature", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[ headers="date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = "wrong date", date = date, authorization = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should with x-date malformed and used for signature but skew test pass", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "x-date: " .. "wrong date" .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[ headers="x-date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = "wrong date", date = date, authorization = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with date malformed and used for signature but skew test pass", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. "wrong date" .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob",algorithm="hmac-sha1",]] .. [[ headers="date content-md5 request-line",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth.com", ["x-date"] = date, date = "wrong date", authorization = hmacAuth, ["content-md5"] = "md5", } }) assert.res_status(200, res) end) 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",]] .. [[headers="date",signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth2.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(200, res) body = cjson.decode(body) assert.equal(hmacAuth, body.headers["authorization"]) assert.equal("bob", body.headers["x-consumer-username"]) assert.equal(credential.username, body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) it("should return 401 when body validation enabled and no digest header is present", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = postBody, headers = { ["HOST"] = "hmacauth4.com", date = date, authorization = hmacAuth, } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("HMAC signature does not match", body.message) end) it("should return 200 when body validation enabled and no body and no digest header is present", 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",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["HOST"] = "hmacauth4.com", date = date, authorization = hmacAuth, } }) assert.res_status(200, res) end) it("should return 200 when body validation enabled and no body and an digest header is present", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local sha256 = resty_sha256:new() sha256:update('') local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["HOST"] = "hmacauth4.com", date = date, digest = digest, authorization = hmacAuth, } }) assert.res_status(200, res) end) it("should pass with invalid credentials and anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth2.com", }, }) local body = assert.res_status(200, res) body = cjson.decode(body) 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("should pass with invalid credentials and username in anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth7.com", }, }) local body = assert.res_status(200, res) body = cjson.decode(body) 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() finally(function() proxy_client = helpers.proxy_client() end) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "hmacauth3.com", }, }) assert.response(res).has.status(500) end) it("should pass with GET when body validation enabled", 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",]] ..[[headers="date",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth4.com", date = date, authorization = hmacAuth, }, }) assert.res_status(200, res) end) it("should pass with POST when body validation enabled and digest header present", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = postBody, headers = { ["HOST"] = "hmacauth4.com", date = date, digest = digest, authorization = hmacAuth, }, }) assert.res_status(200, res) end) it("should pass with POST when body validation enabled but digest header not used", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = postBody, headers = { ["HOST"] = "hmacauth4.com", date = date, digest = digest, authorization = hmacAuth, }, }) assert.res_status(200, res) end) it("should not pass with POST when body validation enabled and digest header missing", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = postBody, headers = { ["HOST"] = "hmacauth4.com", date = date, authorization = hmacAuth, }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("HMAC signature does not match", body.message) end) it("should not pass with POST when body validation enabled and postBody is tampered", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = "abc", headers = { ["HOST"] = "hmacauth4.com", date = date, digest = digest, authorization = hmacAuth, }, }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("HMAC signature does not match", body.message) end) it("should not pass with POST when body validation enabled and digest header is tampered", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local postBody = '{"a":"apple","b":"ball"}' local sha256 = resty_sha256:new() sha256:update(postBody) local digest = "SHA-256=" .. ngx.encode_base64(sha256:final()) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: "..date.."\n".."digest: "..digest)) local hmacAuth = [["hmac username="bob",algorithm="hmac-sha1",]] ..[[headers="date digest",signature="]]..encodedSignature..[["]] local res = assert(proxy_client:send { method = "POST", path = "/request", body = postBody, headers = { ["HOST"] = "hmacauth4.com", date = date, digest = digest .. "spoofed", authorization = hmacAuth, } }) local body = assert.res_status(401, res) body = cjson.decode(body) assert.equal("HMAC signature does not match", body.message) end) it("should pass with GET with request-line", 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should fail with GET with request-line having query param but signed without query param", function() -- hmac-auth signature must include the same query param in request-line: https://github.com/Kong/kong/pull/3339 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" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request?name=foo", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(401, res) 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", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request/?name=foo", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(401, res) end) it("should pass with GET with request-line having query param", 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" .. "\nGET /request?name=foo HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request?name=foo", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request/?name=foo HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request/?name=foo", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with GET with request-line having encoded query param", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local escaped_uri = fmt("/request?name=%s", ngx.escape_uri("foo bar")) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET " .. escaped_uri .. " HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = escaped_uri, body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local json_body = cjson.decode(body) assert.is.equal("foo bar", json_body.uri_args.name) local escaped_uri = fmt("/request?name=%s", ngx.escape_uri("foo bár")) encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET " .. escaped_uri .." HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = escaped_uri, body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local json_body = cjson.decode(body) assert.is.equal("foo bár", json_body.uri_args.name) end) it("should pass with GET with request-line having multiple query params", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local escaped_uri = fmt("/request?name=%s&address=%s" , ngx.escape_uri("foo bar"), ngx.escape_uri("san francisco")) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET " .. escaped_uri .. " HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = escaped_uri, body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local json_body = cjson.decode(body) assert.is.equal("foo bar", json_body.uri_args.name) assert.is.equal("san francisco", json_body.uri_args.address) end) it("should pass with GET with request-line having multiple same query param", 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" .. "\nGET /request?name=foo&name=bar HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request?name=foo&name=bar", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local json_body = cjson.decode(body) assert.is.equal("foo", json_body.uri_args.name[1]) assert.is.equal("bar", json_body.uri_args.name[2]) end) it("should pass with GET with request-line having no uri", 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" .. "\nGET / HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with GET with request-line having encoded path param", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local escaped_uri = fmt("/request/%s/?name=foo&name=bar", ngx.escape_uri("some value")) local encodedSignature = ngx.encode_base64( hmac_sha1_binary("secret", "date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET ".. escaped_uri .. " HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = escaped_uri, body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) local body = assert.res_status(200, res) local json_body = cjson.decode(body) assert.is.equal("foo", json_body.uri_args.name[1]) assert.is.equal("bar", json_body.uri_args.name[2]) assert.is.equal("/request/some value/", json_body.vars.uri) end) it("should fail with GET when enforced header request-line missing", 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", ]] .. [[headers="date content-md5", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(401, res) end) it("should pass with GET with hmac-sha384", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha384"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha384", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should pass with GET with hmac-sha512", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha512"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha512", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth5.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) it("should not pass with hmac-sha512", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha512"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha512", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth6.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(401, res) end) it("should return a 401 with an invalid authorization header", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth6.com", date = date, ["proxy-authorization"] = "this is no hmac token at all is it?", }, }) assert.res_status(401, res) end) it("should pass with hmac-sha1", function() local date = os.date("!%a, %d %b %Y %H:%M:%S GMT") local encodedSignature = ngx.encode_base64( openssl_hmac.new("secret", "sha1"):final("date: " .. date .. "\n" .. "content-md5: md5" .. "\nGET /request HTTP/1.1")) local hmacAuth = [[hmac username="bob", algorithm="hmac-sha1", ]] .. [[headers="date content-md5 request-line", signature="]] .. encodedSignature .. [["]] local res = assert(proxy_client:send { method = "GET", path = "/request", body = {}, headers = { ["HOST"] = "hmacauth6.com", date = date, ["proxy-authorization"] = hmacAuth, ["content-md5"] = "md5", }, }) assert.res_status(200, res) end) end) end) describe("Plugin: hmac-auth (access) [#" .. strategy .. "]", function() local proxy_client local user1 local user2 local anonymous local hmacAuth local hmacDate lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "consumers", "plugins", "hmacauth_credentials", "keyauth_credentials", }) local service1 = bp.services:insert({ path = "/request" }) local route1 = bp.routes:insert { hosts = { "logical-and.com" }, protocols = { "http", "https" }, service = service1 } bp.plugins:insert { name = "hmac-auth", route = { id = route1.id } } bp.plugins:insert { name = "key-auth", route = { id = route1.id } } anonymous = bp.consumers:insert { username = "Anonymous" } user1 = bp.consumers:insert { username = "Mickey" } user2 = bp.consumers:insert { username = "Aladdin" } local service2 = bp.services:insert({ path = "/request" }) local route2 = bp.routes:insert { hosts = { "logical-or.com" }, protocols = { "http", "https" }, service = service2 } bp.plugins:insert { name = "hmac-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 }, } local credential = bp.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({ 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"] = 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, string.format("expected %s or %s, got %s", user1.id, user2.id, 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"] = hmacAuth, ["date"] = hmacDate, }, }) 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"] = 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, string.format("expected %s or %s, got %s", user1.id, user2.id, 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"] = 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(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