Skip to content

Commit

Permalink
Closes #430 and also fixes a problem when starting dnsmasq
Browse files Browse the repository at this point in the history
  • Loading branch information
subnetmarco committed Jul 27, 2015
1 parent 26e72da commit f7da790
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 28 deletions.
17 changes: 14 additions & 3 deletions kong/cli/utils/dnsmasq.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ function _M.stop(kong_config)
end

function _M.start(kong_config)
local cmd = IO.cmd_exists("dnsmasq") and "dnsmasq" or
(IO.cmd_exists("/usr/local/sbin/dnsmasq") and "/usr/local/sbin/dnsmasq" or nil) -- On OS X dnsmasq is at /usr/local/sbin/
local cmd = IO.cmd_exists("dnsmasq") and "dnsmasq"

if not cmd then -- Load dnsmasq given the PATH settings
local env_path = (os.getenv("PATH")..":" or "").."/usr/local/sbin:/usr/sbin" -- Also check in default paths
local paths = stringy.split(env_path, ":")
for _, path in ipairs(paths) do
if IO.file_exists(path..(stringy.endswith(path, "/") and "" or "/").."dnsmasq") then
cmd = path.."/dnsmasq"
break
end
end
end

if not cmd then
cutils.logger:error_exit("Can't find dnsmasq")
end
Expand All @@ -26,7 +37,7 @@ function _M.start(kong_config)
if code ~= 0 then
cutils.logger:error_exit(res)
else
cutils.logger:info("dnsmasq started")
cutils.logger:info("dnsmasq started ("..cmd..")")
end
end

Expand Down
8 changes: 5 additions & 3 deletions kong/plugins/basicauth/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ local function retrieve_credentials(request, conf)

if m and table.getn(m) > 0 then
local decoded_basic = ngx.decode_base64(m[1])
local basic_parts = stringy.split(decoded_basic, ":")
username = basic_parts[1]
password = basic_parts[2]
if decoded_basic then
local basic_parts = stringy.split(decoded_basic, ":")
username = basic_parts[1]
password = basic_parts[2]
end
end
else
ngx.ctx.stop_phases = true
Expand Down
89 changes: 71 additions & 18 deletions kong/plugins/oauth2/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ local REDIRECT_URI = "redirect_uri"
local ACCESS_TOKEN = "access_token"
local GRANT_TYPE = "grant_type"
local GRANT_AUTHORIZATION_CODE = "authorization_code"
local GRANT_CLIENT_CREDENTIALS = "client_credentials"
local GRANT_REFRESH_TOKEN = "refresh_token"
local ERROR = "error"
local AUTHENTICATED_USERID = "authenticated_userid"
Expand Down Expand Up @@ -74,6 +75,24 @@ local function retrieve_parameters()
return utils.table_merge(ngx.req.get_uri_args(), ngx.req.get_post_args())
end

local function retrieve_scopes(parameters, conf)
local scope = parameters[SCOPE]
local scopes = {}
if conf.scopes and scope then
for v in scope:gmatch("%w+") do
if not utils.table_contains(conf.scopes, v) then
return false, {[ERROR] = "invalid_scope", error_description = "\""..v.."\" is an invalid "..SCOPE}
else
table.insert(scopes, v)
end
end
elseif not scope and conf.mandatory_scope then
return false, {[ERROR] = "invalid_scope", error_description = "You must specify a "..SCOPE}
end

return true, scopes
end

local function authorize(conf)
local response_params = {}

Expand All @@ -89,24 +108,14 @@ local function authorize(conf)
else
local response_type = parameters[RESPONSE_TYPE]
-- Check response_type
if not (response_type == CODE or (conf.enable_implicit_grant and response_type == TOKEN)) then -- Authorization Code Grant (http://tools.ietf.org/html/rfc6749#section-4.1.1)
if not ((response_type == CODE and conf.enable_authorization_code) or (conf.enable_implicit_grant and response_type == TOKEN)) then -- Authorization Code Grant (http://tools.ietf.org/html/rfc6749#section-4.1.1)
response_params = {[ERROR] = "unsupported_response_type", error_description = "Invalid "..RESPONSE_TYPE}
end

-- Check scopes
local scope = parameters[SCOPE]
local scopes = {}
if conf.scopes and scope then
for v in scope:gmatch("%w+") do
if not utils.table_contains(conf.scopes, v) then
response_params = {[ERROR] = "invalid_scope", error_description = "\""..v.."\" is an invalid "..SCOPE}
break
else
table.insert(scopes, v)
end
end
elseif not scope and conf.mandatory_scope then
response_params = {[ERROR] = "invalid_scope", error_description = "You must specify a "..SCOPE}
local ok, scopes = retrieve_scopes(parameters, conf)
if not ok then
response_params = scopes -- If it's not ok, then this is the error message
end

-- Check client_id and redirect_uri
Expand Down Expand Up @@ -154,27 +163,63 @@ local function authorize(conf)
})
end

local function retrieve_client_credentials(parameters)
local client_id, client_secret
local authorization_header = ngx.req.get_headers()["authorization"]
if parameters[CLIENT_ID] then
client_id = parameters[CLIENT_ID]
client_secret = parameters[CLIENT_SECRET]
elseif authorization_header then
local iterator, iter_err = ngx.re.gmatch(authorization_header, "\\s*[Bb]asic\\s*(.+)")
if not iterator then
ngx.log(ngx.ERR, iter_err)
return
end

local m, err = iterator()
if err then
ngx.log(ngx.ERR, err)
return
end

if m and table.getn(m) > 0 then
local decoded_basic = ngx.decode_base64(m[1])
if decoded_basic then
local basic_parts = stringy.split(decoded_basic, ":")
client_id = basic_parts[1]
client_secret = basic_parts[2]

print(client_id)
print(client_secret)
end
end
end

return client_id, client_secret
end

local function issue_token(conf)
local response_params = {}

local parameters = retrieve_parameters() --TODO: Also from authorization header
local state = parameters[STATE]

local grant_type = parameters[GRANT_TYPE]
if not (grant_type == GRANT_AUTHORIZATION_CODE or grant_type == GRANT_REFRESH_TOKEN) then
if not (grant_type == GRANT_AUTHORIZATION_CODE or grant_type == GRANT_REFRESH_TOKEN or (conf.enable_client_credentials and grant_type == GRANT_CLIENT_CREDENTIALS)) then
response_params = {[ERROR] = "invalid_request", error_description = "Invalid "..GRANT_TYPE}
end

local client_id, client_secret = retrieve_client_credentials(parameters)

-- Check client_id and redirect_uri
local redirect_uri, client = get_redirect_uri(parameters[CLIENT_ID])
local redirect_uri, client = get_redirect_uri(client_id)
if not redirect_uri then
response_params = {[ERROR] = "invalid_request", error_description = "Invalid "..CLIENT_ID}
elseif parameters[REDIRECT_URI] and parameters[REDIRECT_URI] ~= redirect_uri then
response_params = {[ERROR] = "invalid_request", error_description = "Invalid "..REDIRECT_URI.." that does not match with the one created with the application"}
end

local client_secret = parameters[CLIENT_SECRET]
if not client_secret or (client and client_secret ~= client.client_secret) then
if client and client.client_secret ~= client_secret then
response_params = {[ERROR] = "invalid_request", error_description = "Invalid "..CLIENT_SECRET}
end

Expand All @@ -187,6 +232,14 @@ local function issue_token(conf)
else
response_params = generate_token(conf, client, authorization_code.authenticated_userid, authorization_code.scope, state)
end
elseif grant_type == GRANT_CLIENT_CREDENTIALS then
-- Check scopes
local ok, scopes = retrieve_scopes(parameters, conf)
if not ok then
response_params = scopes -- If it's not ok, then this is the error message
else
response_params = generate_token(conf, client, nil, table.concat(scopes, " "), state)
end
elseif grant_type == GRANT_REFRESH_TOKEN then
local refresh_token = parameters[REFRESH_TOKEN]
local token = refresh_token and dao.oauth2_tokens:find_by_keys({refresh_token = refresh_token})[1] or nil
Expand Down
2 changes: 2 additions & 0 deletions kong/plugins/oauth2/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ return {
mandatory_scope = { required = true, type = "boolean", default = false, func = check_mandatory_scope },
provision_key = { required = false, unique = true, type = "string", func = generate_if_missing },
token_expiration = { required = true, type = "number", default = 7200 },
enable_authorization_code = { required = true, type = "boolean", default = true },
enable_implicit_grant = { required = true, type = "boolean", default = false },
enable_client_credentials = { required = true, type = "boolean", default = false },
hide_credentials = { type = "boolean", default = false }
}
}
63 changes: 59 additions & 4 deletions spec/plugins/oauth2/access_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,17 @@ describe("Authentication Plugin", function()
api = {
{ name = "tests oauth2", public_dns = "oauth2.com", target_url = "http://mockbin.com" },
{ name = "tests oauth2 with path", public_dns = "mockbin-path.com", target_url = "http://mockbin.com", path = "/somepath/" },
{ name = "tests oauth2 with hide credentials", public_dns = "oauth2_3.com", target_url = "http://mockbin.com" }
{ name = "tests oauth2 with hide credentials", public_dns = "oauth2_3.com", target_url = "http://mockbin.com" },
{ name = "tests oauth2 client credentials", public_dns = "oauth2_4.com", target_url = "http://mockbin.com" },
},
consumer = {
{ username = "auth_tests_consumer" }
},
plugin_configuration = {
{ name = "oauth2", value = { scopes = { "email", "profile" }, mandatory_scope = true, provision_key = "provision123", token_expiration = 5, enable_implicit_grant = true }, __api = 1 },
{ name = "oauth2", value = { scopes = { "email", "profile" }, mandatory_scope = true, provision_key = "provision123", token_expiration = 5, enable_implicit_grant = true }, __api = 2 },
{ name = "oauth2", value = { scopes = { "email", "profile" }, mandatory_scope = true, provision_key = "provision123", token_expiration = 5, enable_implicit_grant = true, hide_credentials = true }, __api = 3 }
{ name = "oauth2", value = { scopes = { "email", "profile" }, mandatory_scope = true, provision_key = "provision123", token_expiration = 5, enable_implicit_grant = true, hide_credentials = true }, __api = 3 },
{ name = "oauth2", value = { scopes = { "email", "profile" }, mandatory_scope = true, provision_key = "provision123", token_expiration = 5, enable_client_credentials = true, enable_authorization_code = false }, __api = 4 },
},
oauth2_credential = {
{ client_id = "clientid123", client_secret = "secret123", redirect_uri = "http://google.com/kong", name="testapp", __consumer = 1 }
Expand Down Expand Up @@ -252,6 +254,59 @@ describe("Authentication Plugin", function()
end)

end)

describe("Client Credentials", function()

it("should return an error when client_secret is not sent", function()
local response, status, headers = http_client.post(PROXY_URL.."/oauth2/token", { client_id = "clientid123", scope = "email", response_type = "token" }, {host = "oauth2_4.com"})
local body = cjson.decode(response)
assert.are.equal(400, status)
assert.are.equal(2, utils.table_size(body))
assert.are.equal("invalid_request", body.error)
assert.are.equal("Invalid client_secret", body.error_description)
end)

it("should return an error when client_secret is not sent", function()
local response, status, headers = http_client.post(PROXY_URL.."/oauth2/token", { client_id = "clientid123", client_secret="secret123", scope = "email", response_type = "token" }, {host = "oauth2_4.com"})
local body = cjson.decode(response)
assert.are.equal(400, status)
assert.are.equal(2, utils.table_size(body))
assert.are.equal("invalid_request", body.error)
assert.are.equal("Invalid grant_type", body.error_description)
end)

it("should return success", function()
local response, status, headers = http_client.post(PROXY_URL.."/oauth2/token", { client_id = "clientid123", client_secret="secret123", scope = "email", grant_type = "client_credentials" }, {host = "oauth2_4.com"})
local body = cjson.decode(response)
assert.are.equal(200, status)
assert.are.equals(4, utils.table_size(body))
assert.truthy(body.refresh_token)
assert.truthy(body.access_token)
assert.are.equal("bearer", body.token_type)
assert.are.equal(5, body.expires_in)
end)

it("should return success with authorization header", function()
local response, status, headers = http_client.post(PROXY_URL.."/oauth2/token", { scope = "email", grant_type = "client_credentials" }, {host = "oauth2_4.com", authorization = "Basic Y2xpZW50aWQxMjM6c2VjcmV0MTIz"})
local body = cjson.decode(response)
assert.are.equal(200, status)
assert.are.equals(4, utils.table_size(body))
assert.truthy(body.refresh_token)
assert.truthy(body.access_token)
assert.are.equal("bearer", body.token_type)
assert.are.equal(5, body.expires_in)
end)

it("should return an error with a wrong authorization header", function()
local response, status, headers = http_client.post(PROXY_URL.."/oauth2/token", { scope = "email", grant_type = "client_credentials" }, {host = "oauth2_4.com", authorization = "Basic Y2xpZW50aWQxMjM6c2VjcmV0MTI0"})
local body = cjson.decode(response)
assert.are.equal(400, status)
assert.are.equal(2, utils.table_size(body))
assert.are.equal("invalid_request", body.error)
assert.are.equal("Invalid client_secret", body.error_description)
end)

end)
end)

describe("OAuth2 Access Token", function()
Expand All @@ -262,7 +317,7 @@ describe("Authentication Plugin", function()
assert.are.equal(400, status)
assert.are.equal(2, utils.table_size(body))
assert.are.equal("invalid_request", body.error)
assert.are.equal("Invalid client_secret", body.error_description)
assert.are.equal("Invalid client_id", body.error_description)

-- Checking headers
assert.are.equal("no-store", headers["cache-control"])
Expand All @@ -277,7 +332,7 @@ describe("Authentication Plugin", function()
assert.are.equal(400, status)
assert.are.equal(2, utils.table_size(body))
assert.are.equal("invalid_request", body.error)
assert.are.equal("Invalid client_secret", body.error_description)
assert.are.equal("Invalid client_id", body.error_description)

-- Checking headers
assert.are.equal("no-store", headers["cache-control"])
Expand Down

0 comments on commit f7da790

Please sign in to comment.