Skip to content

Commit 62948b7

Browse files
authored
feat: add test (#3)
1 parent 81d8edc commit 62948b7

File tree

3 files changed

+518
-1
lines changed

3 files changed

+518
-1
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ jobs:
3535
- name: script
3636
run: |
3737
sudo docker run --rm --name keycloak -d -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v $PWD/t/test-realm-keycloak-18.0.2.json:/opt/keycloak/data/import/realm.json quay.io/keycloak/keycloak:18.0.2 start-dev --import-realm
38-
sleep 8
38+
sleep 30
3939
export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$PATH
4040
make test

t/lib/keycloak.lua

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
--
2+
-- Licensed to the Apache Software Foundation (ASF) under one or more
3+
-- contributor license agreements. See the NOTICE file distributed with
4+
-- this work for additional information regarding copyright ownership.
5+
-- The ASF licenses this file to You under the Apache License, Version 2.0
6+
-- (the "License"); you may not use this file except in compliance with
7+
-- the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing, software
12+
-- distributed under the License is distributed on an "AS IS" BASIS,
13+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
-- See the License for the specific language governing permissions and
15+
-- limitations under the License.
16+
--
17+
local http = require "resty.http"
18+
19+
local _M = {}
20+
21+
22+
-- Login keycloak and return the login original uri
23+
function _M.login_keycloak(uri, username, password)
24+
local httpc = http.new()
25+
26+
local res, err = httpc:request_uri(uri, {method = "GET"})
27+
if not res then
28+
return nil, err
29+
elseif res.status ~= 302 then
30+
return nil, "login was not redirected to keycloak."
31+
else
32+
local cookies = res.headers['Set-Cookie']
33+
local cookie_str = _M.concatenate_cookies(cookies)
34+
35+
res, err = httpc:request_uri(res.headers['Location'], {method = "GET"})
36+
if not res then
37+
-- No response, must be an error.
38+
return nil, err
39+
elseif res.status ~= 200 then
40+
-- Unexpected response.
41+
return nil, res.body
42+
end
43+
44+
-- From the returned form, extract the submit URI and parameters.
45+
local uri, params = res.body:match('.*action="(.*)%?(.*)" method="post">')
46+
47+
-- Substitute escaped ampersand in parameters.
48+
params = params:gsub("&", "&")
49+
50+
local auth_cookies = res.headers['Set-Cookie']
51+
52+
-- Concatenate cookies into one string as expected when sent in request header.
53+
local auth_cookie_str = _M.concatenate_cookies(auth_cookies)
54+
55+
-- Invoke the submit URI with parameters and cookies, adding username
56+
-- and password in the body.
57+
-- Note: Username and password are specific to the Keycloak Docker image used.
58+
res, err = httpc:request_uri(uri .. "?" .. params, {
59+
method = "POST",
60+
body = "username=" .. username .. "&password=" .. password,
61+
headers = {
62+
["Content-Type"] = "application/x-www-form-urlencoded",
63+
["Cookie"] = auth_cookie_str
64+
}
65+
})
66+
if not res then
67+
-- No response, must be an error.
68+
return nil, err
69+
elseif res.status ~= 302 then
70+
-- Not a redirect which we expect.
71+
return nil, "Login form submission did not return redirect to redirect URI."
72+
end
73+
74+
local keycloak_cookie_str = _M.concatenate_cookies(res.headers['Set-Cookie'])
75+
76+
-- login callback
77+
local redirect_uri = res.headers['Location']
78+
res, err = httpc:request_uri(redirect_uri, {
79+
method = "GET",
80+
headers = {
81+
["Cookie"] = cookie_str
82+
}
83+
})
84+
85+
if not res then
86+
-- No response, must be an error.
87+
return nil, err
88+
elseif res.status ~= 302 then
89+
-- Not a redirect which we expect.
90+
return nil, "login callback: " ..
91+
"did not return redirect to original URI."
92+
end
93+
94+
cookies = res.headers['Set-Cookie']
95+
cookie_str = _M.concatenate_cookies(cookies)
96+
97+
return res, nil, cookie_str, keycloak_cookie_str
98+
end
99+
end
100+
101+
-- Login keycloak and return the login original uri
102+
function _M.login_keycloak_for_second_sp(uri, keycloak_cookie_str)
103+
local httpc = http.new()
104+
105+
local res, err = httpc:request_uri(uri, {method = "GET"})
106+
if not res then
107+
return nil, err
108+
elseif res.status ~= 302 then
109+
return nil, "login was not redirected to keycloak."
110+
end
111+
112+
local cookies = res.headers['Set-Cookie']
113+
local cookie_str = _M.concatenate_cookies(cookies)
114+
115+
res, err = httpc:request_uri(res.headers['Location'], {
116+
method = "GET",
117+
headers = {
118+
["Cookie"] = keycloak_cookie_str
119+
}
120+
})
121+
ngx.log(ngx.INFO, keycloak_cookie_str)
122+
123+
if not res then
124+
-- No response, must be an error.
125+
return nil, err
126+
elseif res.status ~= 302 then
127+
-- Not a redirect which we expect.
128+
return nil, res.body
129+
end
130+
131+
-- login callback
132+
res, err = httpc:request_uri(res.headers['Location'], {
133+
method = "GET",
134+
headers = {
135+
["Cookie"] = cookie_str
136+
}
137+
})
138+
139+
if not res then
140+
-- No response, must be an error.
141+
return nil, err
142+
elseif res.status ~= 302 then
143+
-- Not a redirect which we expect.
144+
return nil, "login callback: " ..
145+
"did not return redirect to original URI."
146+
end
147+
148+
cookies = res.headers['Set-Cookie']
149+
cookie_str = _M.concatenate_cookies(cookies)
150+
151+
return res, nil, cookie_str
152+
end
153+
154+
-- Logout keycloak and return the logout_redirect_uri
155+
function _M.logout_keycloak(uri, cookie_str, keycloak_cookie_str)
156+
local httpc = http.new()
157+
158+
local res, err = httpc:request_uri(uri, {
159+
method = "GET",
160+
headers = {
161+
["Cookie"] = cookie_str
162+
}
163+
})
164+
165+
if not res then
166+
return nil, err
167+
elseif res.status ~= 302 then
168+
return nil, "logout was not redirected to keycloak."
169+
else
170+
-- keycloak logout
171+
res, err = httpc:request_uri(res.headers['Location'], {
172+
method = "GET",
173+
headers = {
174+
["Cookie"] = keycloak_cookie_str
175+
}
176+
})
177+
if not res then
178+
-- No response, must be an error.
179+
return nil, err
180+
elseif res.status ~= 302 then
181+
-- Not a redirect which we expect.
182+
return nil, "Logout did not return redirect to redirect URI."
183+
end
184+
185+
-- logout callback
186+
res, err = httpc:request_uri(res.headers['Location'], {
187+
method = "GET",
188+
headers = {
189+
["Cookie"] = cookie_str
190+
}
191+
})
192+
193+
if not res then
194+
-- No response, must be an error.
195+
return nil, err
196+
elseif res.status ~= 302 then
197+
-- Not a redirect which we expect.
198+
return nil, "logout callback: " ..
199+
"did not return redirect to logout redirect URI."
200+
end
201+
202+
return res, nil
203+
end
204+
end
205+
206+
-- Logout keycloak and return the logout_redirect_uri
207+
function _M.single_logout(uri, cookie_str, cookie_str2, keycloak_cookie_str)
208+
local httpc = http.new()
209+
210+
local res, err = httpc:request_uri(uri, {
211+
method = "GET",
212+
headers = {
213+
["Cookie"] = cookie_str
214+
}
215+
})
216+
217+
if not res then
218+
return nil, err
219+
elseif res.status ~= 302 then
220+
return nil, "logout was not redirected to keycloak."
221+
end
222+
223+
-- logout request from sp1 to keycloak
224+
res, err = httpc:request_uri(res.headers['Location'], {
225+
method = "GET",
226+
headers = {
227+
["Cookie"] = keycloak_cookie_str
228+
}
229+
})
230+
if not res then
231+
-- No response, must be an error.
232+
return nil, err
233+
elseif res.status ~= 302 then
234+
-- Not a redirect which we expect.
235+
return nil, "Logout did not return redirect to redirect URI."
236+
end
237+
238+
-- logout callback to sp2
239+
res, err = httpc:request_uri(res.headers['Location'], {
240+
method = "GET",
241+
headers = {
242+
["Cookie"] = cookie_str2
243+
}
244+
})
245+
246+
if not res then
247+
-- No response, must be an error.
248+
return nil, err
249+
elseif res.status ~= 302 then
250+
-- Not a redirect which we expect.
251+
return nil, "logout callback: " ..
252+
"did not return redirect to logout redirect URI."
253+
end
254+
255+
-- logout response from sp2 to keycloak
256+
res, err = httpc:request_uri(res.headers['Location'], {
257+
method = "GET",
258+
headers = {
259+
["Cookie"] = keycloak_cookie_str
260+
}
261+
})
262+
if not res then
263+
-- No response, must be an error.
264+
return nil, err
265+
elseif res.status ~= 302 then
266+
-- Not a redirect which we expect.
267+
return nil, "Logout did not return redirect to redirect URI."
268+
end
269+
270+
-- logout response from keycloak to sp1
271+
res, err = httpc:request_uri(res.headers['Location'], {
272+
method = "GET",
273+
headers = {
274+
["Cookie"] = cookie_str
275+
}
276+
})
277+
278+
if not res then
279+
-- No response, must be an error.
280+
return nil, err
281+
elseif res.status ~= 302 then
282+
-- Not a redirect which we expect.
283+
return nil, "logout callback: " ..
284+
"did not return redirect to logout redirect URI."
285+
end
286+
287+
return res, nil
288+
end
289+
290+
-- Concatenate cookies into one string as expected when sent in request header.
291+
function _M.concatenate_cookies(cookies)
292+
local cookie_str = ""
293+
if type(cookies) == 'string' then
294+
cookie_str = cookies:match('([^;]*); .*')
295+
else
296+
-- Must be a table.
297+
local len = #cookies
298+
if len > 0 then
299+
cookie_str = cookies[1]:match('([^;]*); .*')
300+
for i = 2, len do
301+
cookie_str = cookie_str .. "; " .. cookies[i]:match('([^;]*); .*')
302+
end
303+
end
304+
end
305+
306+
return cookie_str, nil
307+
end
308+
309+
310+
return _M

0 commit comments

Comments
 (0)