Skip to content
This repository was archived by the owner on Oct 12, 2023. It is now read-only.

Commit 0ff3204

Browse files
committed
Fix retrival of public keys when keycloak running older version or is misconfigured
1 parent 126bccf commit 0ff3204

10 files changed

+89
-32
lines changed

Makefile

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ include makefiles/*.mk
22

33
REPOSITORY?=gbbirkisson
44
IMAGE?=kong-plugin-jwt-keycloak
5-
KONG_VERSION?=1.0
5+
KONG_VERSION?=1.1.2
66
FULL_IMAGE_NAME:=${REPOSITORY}/${IMAGE}:${KONG_VERSION}
77

8-
PLUGIN_VERSION?=1.0.1-1
8+
PLUGIN_VERSION?=1.0.2-1
99

10-
TEST_VERSIONS?=1.0.0 1.0.1 1.0.2 1.0.3 1.1rc1
10+
TEST_VERSIONS?=1.0.3 1.1.2
1111

1212
### Docker ###
1313

@@ -40,7 +40,7 @@ test-unit:
4040
@echo "Running unit tests"
4141
@echo
4242

43-
@cd tests && $(MAKE) --no-print-directory _tests-unit PLUGIN_VERSION=${PLUGIN_VERSION}
43+
@cd tests && $(MAKE) --no-print-directory _tests-unit PLUGIN_VERSION=${PLUGIN_VERSION} KONG_VERSION=${KONG_VERSION}
4444

4545
@echo
4646
@echo "Unit tests passed"

README.md

+13-6
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,17 @@ If you have any suggestion or comments, please feel free to open an issue on thi
4040

4141
| Kong Version | Tests passing |
4242
| ------------ | :----------------: |
43-
| Kong 0.13.x | :x: |
44-
| Kong 0.14.x | :x: |
45-
| Kong 1.0.x | :white_check_mark: |
46-
| Kong 1.1.rc1 | :white_check_mark: |
43+
| 0.13.x | :x: |
44+
| 0.14.x | :x: |
45+
| 1.0.x | :white_check_mark: |
46+
| 1.1.x | :white_check_mark: |
47+
48+
| Keycloak Version | Tests passing |
49+
| ---------------- | :----------------: |
50+
| 3.X.X | :white_check_mark: |
51+
| 4.X.X | :white_check_mark: |
52+
| 5.X.X | :white_check_mark: |
53+
| 6.X.X | :white_check_mark: |
4754

4855
## Installation
4956

@@ -118,10 +125,10 @@ curl -X POST http://localhost:8001/plugins \
118125
| config.uri_param_names | no | `jwt` | A list of querystring parameters that Kong will inspect to retrieve JWTs. |
119126
| config.cookie_names | no | | A list of cookie names that Kong will inspect to retrieve JWTs. |
120127
| config.claims_to_verify | no | `exp` | A list of registered claims (according to [RFC 7519](https://tools.ietf.org/html/rfc7519)) that Kong can verify as well. Accepted values: `exp`, `nbf`. |
121-
| config.anonymous | no | | An optional string (consumer uuid) value to use as an “anonymous” consumer if authentication fails. If empty (default), the request will fail with an authentication failure `4xx`. Please note that this value must refer to the Consumer `id` attribute which is internal to Kong, and not its `custom_id`. |
128+
| config.anonymous | no | | An optional string (consumer uuid) value to use as an “anonymous” consumer if authentication fails. If empty (default), the request will fail with an authentication failure `4xx`. Please note that this value must refer to the Consumer `id` attribute which is internal to Kong, and not its `custom_id`. |
122129
| config.run_on_preflight | no | `true` | A boolean value that indicates whether the plugin should run (and try to authenticate) on `OPTIONS` preflight requests, if set to false then `OPTIONS` requests will always be allowed. |
123130
| config.maximum_expiration | no | `0` | An integer limiting the lifetime of the JWT to `maximum_expiration` seconds in the future. Any JWT that has a longer lifetime will rejected (HTTP 403). If this value is specified, `exp` must be specified as well in the `claims_to_verify` property. The default value of `0` represents an indefinite period. Potential clock skew should be considered when configuring this value. |
124-
| config.algorithm | no | `RS256` | The algorithm used to verify the token’s signature. Can be `HS256`, `HS384`, `HS512`, `RS256`, or `ES256`. |
131+
| config.algorithm | no | `RS256` | The algorithm used to verify the token’s signature. Can be `HS256`, `HS384`, `HS512`, `RS256`, or `ES256`. |
125132
| config.allowed_iss | yes | | A list of allowed issuers for this route/service/api. |
126133
| config.iss_key_grace_period | no | `10` | An integer that sets the number of seconds until public keys for an issuer can be updated after writing new keys to the cache. This is a guard so that the Kong cache will not invalidate every time a token signed with an invalid public key is sent to the plugin. |
127134
| config.well_known_template | false | *see description* | A string template that the well known endpoint for keycloak is created from. String formatting is applied on the template and `%s` is replaced by the issuer of the token. Default value is `%s/.well-known/openid-configuration` |

kong-plugin-jwt-keycloak-1.0.1-1.rockspec kong-plugin-jwt-keycloak-1.0.2-1.rockspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package = "kong-plugin-jwt-keycloak"
22

3-
version = "1.0.1-1"
3+
version = "1.0.2-1"
44
-- The version '0.1.0' is the source code version, the trailing '1' is the version of this rockspec.
55
-- whenever the source version changes, the rockspec should be reset to 1. The rockspec version is only
66
-- updated (incremented) when this file changes, but the source remains the same.
@@ -10,7 +10,7 @@ supported_platforms = {"linux", "macosx"}
1010

1111
source = {
1212
url = "git://github.com/gbbirkisson/kong-plugin-jwt-keycloak",
13-
tag = "v1.0.1",
13+
tag = "v1.0.2",
1414
}
1515
description = {
1616
summary = "A Kong plugin that will validate tokens issued by keycloak",

luarocks.Dockerfile

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM kong:1.0 as packer
1+
FROM kong:1.1.2 as builder
22

33
ENV LUAROCKS_MODULE=kong-plugin-jwt-keycloak
44

@@ -7,10 +7,10 @@ RUN apk add --no-cache git zip && \
77
luarocks install ${LUAROCKS_MODULE} && \
88
luarocks pack ${LUAROCKS_MODULE}
99

10-
FROM kong:1.0
10+
FROM kong:1.1.2
1111

1212
ENV KONG_PLUGINS="bundled,jwt-keycloak"
1313

14-
COPY --from=packer kong-plugin-jwt-keycloak* /tmp/
15-
RUN luarocks install /tmp/kong-plugin-jwt-keycloak* &&\
14+
COPY --from=builder kong-plugin-jwt-keycloak* /tmp/
15+
RUN luarocks install /tmp/kong-plugin-jwt-keycloak* && \
1616
rm /tmp/*

makefiles/keycloak.mk

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KEYCLOAK_IMAGE:=jboss/keycloak:4.8.3.Final
1+
KEYCLOAK_IMAGE:=jboss/keycloak:6.0.1
22
KEYCLOAK_CONTAINER_NAME:=kc_local
33
KEYCLOAK_PORT:=8080
44
KEYCLOAK_ADMIN_USER:=admin
@@ -9,9 +9,6 @@ keycloak-start:
99
-- @docker run -d \
1010
--name ${KEYCLOAK_CONTAINER_NAME} \
1111
-p ${KEYCLOAK_PORT}:8080 \
12-
-e KEYCLOAK_HOSTNAME=localhost \
13-
-e KEYCLOAK_HTTP_PORT=8080 \
14-
-e KEYCLOAK_PORT=${KEYCLOAK_PORT} \
1512
-e KEYCLOAK_USER=${KEYCLOAK_ADMIN_USER} \
1613
-e KEYCLOAK_PASSWORD=${KEYCLOAK_ADMIN_PASS} \
1714
${KEYCLOAK_IMAGE}

src/keycloak_keys.lua

+45-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,53 @@ local http = require "socket.http"
22
local cjson_safe = require "cjson.safe"
33
local convert = require "kong.plugins.jwt-keycloak.key_conversion"
44

5-
local function get_request(url)
5+
local function parse_url(url)
6+
local chunk, protocol = url:match("^(([a-z0-9+]+)://)")
7+
url = url:sub((chunk and #chunk or 0) + 1)
8+
9+
local auth
10+
chunk, auth = url:match('(([%w%p]+:?[%w%p]+)@)')
11+
url = url:sub((chunk and #chunk or 0) + 1)
12+
13+
local host
14+
local hostname
15+
local port
16+
if protocol then
17+
host = url:match("^([%a%.%d-]+:?%d*)")
18+
if host then
19+
hostname = host:match("^([^:/]+)")
20+
port = host:match(":(%d+)$")
21+
end
22+
url = url:sub((host and #host or 0) + 1)
23+
end
24+
25+
local parsed = {
26+
protocol = protocol,
27+
host = host,
28+
hostname = hostname,
29+
port = port,
30+
}
31+
32+
return parsed
33+
end
34+
35+
local function get_request(url, port)
636
local res
737
local status
38+
local err
839

9-
res, status = http.request(url)
40+
local chunks = {}
41+
res, status = http.request{
42+
url = url,
43+
port = port,
44+
sink = ltn12.sink.table(chunks)
45+
}
1046

1147
if status ~= 200 then
1248
return nil, 'Failed calling url ' .. url
1349
end
1450

15-
res, err = cjson_safe.decode(res)
51+
res, err = cjson_safe.decode(table.concat(chunks))
1652
if not res then
1753
return nil, 'Failed to parse json response'
1854
end
@@ -25,17 +61,20 @@ local function get_wellknown_endpoint(well_known_template, issuer)
2561
end
2662

2763
local function get_issuer_keys(well_known_endpoint)
28-
local res, err = get_request(well_known_endpoint)
64+
-- Get port of the request: This is done because keycloak 3.X.X does not play well with lua socket.http
65+
local req = parse_url(well_known_endpoint)
66+
67+
local res, err = get_request(well_known_endpoint, req.port)
2968
if err then
3069
return nil, err
3170
end
3271

33-
local res, err = get_request(res['jwks_uri'])
72+
local res, err = get_request(res['jwks_uri'], req.port)
3473
if err then
3574
return nil, err
3675
end
3776

38-
keys = {}
77+
local keys = {}
3978
for i, key in ipairs(res['keys']) do
4079
keys[i] = string.gsub(
4180
convert.convert_kc_key(key),

tests/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ UNIT_TEST_IMAGE:=jwt-keycloak-unit-test-image
22
INTE_TEST_IMAGE:=jwt-keycloak-inte-test-image
33

44
_build-unit-test-image:
5-
@docker build -t ${UNIT_TEST_IMAGE} --build-arg PLUGIN_VERSION=${PLUGIN_VERSION} -f unit_tests/Dockerfile ..
5+
@docker build -t ${UNIT_TEST_IMAGE} --build-arg PLUGIN_VERSION=${PLUGIN_VERSION} --build-arg KONG_VERSION=${KONG_VERSION} -f unit_tests/Dockerfile ..
66

77
_tests-unit: _build-unit-test-image
88
@docker run -it --rm --net=host -v ${PWD}:/jwt-keycloak:ro ${UNIT_TEST_IMAGE}

tests/integration_tests/tests/TestRoles.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ class TestRoles(unittest.TestCase):
1111
def test_no_auth(self, status, body):
1212
self.assertEqual(OK, status)
1313

14-
@skip("New keycloak handles roles differently")
14+
@skip("Need to update tests")
1515
@create_api({
1616
'allowed_iss': ['http://localhost:8080/auth/realms/master'],
1717
'roles': ['test_role']
1818
})
1919
@authenticate()
2020
@call_api()
2121
def test_roles_auth(self, status, body):
22+
if not KC_VERSION.startswith('3'):
23+
self.skipTest("Test not supported for " + KC_VERSION)
2224
self.assertEqual(OK, status)
2325

2426
@create_api({
@@ -31,14 +33,16 @@ def test_roles_auth_rainy(self, status, body):
3133
self.assertEqual(FORBIDDEN, status)
3234
self.assertEqual('Access token does not have the required scope/role', body.get('message'))
3335

34-
@skip("New keycloak handles roles differently")
36+
@skip("Need to update tests")
3537
@create_api({
3638
'allowed_iss': ['http://localhost:8080/auth/realms/master'],
3739
'roles': ['test_role', 'not_found']
3840
})
3941
@authenticate()
4042
@call_api()
4143
def test_roles_auth_double(self, status, body):
44+
if not KC_VERSION.startswith('3'):
45+
self.skipTest("Test not supported for " + KC_VERSION)
4246
self.assertEqual(OK, status)
4347

4448
@create_api({
@@ -114,6 +118,8 @@ def test_client_roles_auth(self, status, body):
114118
@authenticate()
115119
@call_api()
116120
def test_client_scope(self, status, body):
121+
if KC_VERSION.startswith('3'):
122+
self.skipTest("Test not supported for " + KC_VERSION)
117123
self.assertEqual(OK, status)
118124

119125
@create_api({
@@ -123,6 +129,8 @@ def test_client_scope(self, status, body):
123129
@authenticate()
124130
@call_api()
125131
def test_client_scope_double(self, status, body):
132+
if KC_VERSION.startswith('3'):
133+
self.skipTest("Test not supported for " + KC_VERSION)
126134
self.assertEqual(OK, status)
127135

128136
@create_api({

tests/integration_tests/tests/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@
2222

2323
assert r.status_code == 200
2424
KC_ADMIN_TOKEN = r.json()['access_token']
25+
26+
r = requests.get(KC_HOST + '/admin/serverinfo', headers={'Authorization': 'Bearer ' + KC_ADMIN_TOKEN})
27+
assert r.status_code == 200
28+
KC_VERSION = r.json()['systemInfo']['version']

tests/unit_tests/Dockerfile

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM ubuntu
1+
FROM ubuntu:18.04
22

33
# Install openresty and luarocks
44
RUN apt-get update -y && apt-get install -yqq wget software-properties-common
@@ -7,13 +7,15 @@ RUN add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release
77
RUN apt-get update -y && apt-get install -yqq openresty luarocks
88

99
# Install dependencies for rocks to be installed
10-
RUN apt-get install -yqq git libssl-dev m4 # for cqueues
10+
RUN apt-get install -yqq git libssl-dev m4 libyaml-dev
1111
RUN git config --global url.https://github.com/.insteadOf git://github.com/
1212

1313
# Install kong and busted
14-
RUN luarocks install kong
1514
RUN luarocks install busted
1615

16+
ARG KONG_VERSION
17+
RUN luarocks install kong ${KONG_VERSION}-0
18+
1719
# Install plugin
1820
COPY ./*.rockspec /tmp
1921
COPY ./LICENSE /tmp/LICENSE

0 commit comments

Comments
 (0)