Compare commits
544
Commits
4.0.0.Beta2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfafe65814 | ||
|
|
1b8dc04459 | ||
|
|
4355c89b9d | ||
|
|
1b37394276 | ||
|
|
ded82fff3d | ||
|
|
4fbbaf18aa | ||
|
|
5ad929b9ee | ||
|
|
2b9b1ba45f | ||
|
|
8b122de425 | ||
|
|
7d75377813 | ||
|
|
3c8dddf33e | ||
|
|
311e848460 | ||
|
|
607bb1b995 | ||
|
|
91637120ee | ||
|
|
d90a5d1367 | ||
|
|
d395043fc7 | ||
|
|
c9cd060417 | ||
|
|
abd6e560ac | ||
|
|
6e93ca36af | ||
|
|
2d727fc54c | ||
|
|
6db1f60e27 | ||
|
|
8af1ca8fc3 | ||
|
|
55f90ff09f | ||
|
|
dc06a8cee3 | ||
|
|
f3bf1456ab | ||
|
|
548950ed8e | ||
|
|
f67d6f9660 | ||
|
|
a1d86fbce8 | ||
|
|
3756cf629b | ||
|
|
0793234c19 | ||
|
|
461dae20de | ||
|
|
a3d4612edd | ||
|
|
0533782d90 | ||
|
|
3bdbbf41af | ||
|
|
0b36020bf5 | ||
|
|
85f11873c3 | ||
|
|
39bf08e1b9 | ||
|
|
9f88abb022 | ||
|
|
78b2067f73 | ||
|
|
cf57a1bc4b | ||
|
|
8c650f9f6a | ||
|
|
22d8fb17f5 | ||
|
|
6cee8b126b | ||
|
|
f5ae76d8e3 | ||
|
|
e391328b5d | ||
|
|
ecd476fb10 | ||
|
|
c3778e66db | ||
|
|
6a23eb19f5 | ||
|
|
72b23c1357 | ||
|
|
0cb6053699 | ||
|
|
6564cebc0f | ||
|
|
a957e118e6 | ||
|
|
0897d969b1 | ||
|
|
1b5a83c4f1 | ||
|
|
f090b39e85 | ||
|
|
5b72bd8da6 | ||
|
|
602a6e201d | ||
|
|
086dbae5ca | ||
|
|
4f5b41c65b | ||
|
|
28eb1007fb | ||
|
|
7703d81389 | ||
|
|
cd96d6cc35 | ||
|
|
1ee6fd7130 | ||
|
|
6450a457d0 | ||
|
|
6d04247947 | ||
|
|
29f8187978 | ||
|
|
e7b7307b4f | ||
|
|
5e1d85c9a8 | ||
|
|
560d76b7ee | ||
|
|
bce2aee144 | ||
|
|
94f2ce2eae | ||
|
|
3be2c35561 | ||
|
|
cbe59f03b7 | ||
|
|
36b0d8b80e | ||
|
|
5880efe775 | ||
|
|
e6b9364c39 | ||
|
|
0c6b20e862 | ||
|
|
327991bd73 | ||
|
|
234b7a06a1 | ||
|
|
00cee1d8cf | ||
|
|
d0f75c8766 | ||
|
|
b2aa324ee4 | ||
|
|
76e4253a21 | ||
|
|
ffcd8e09e7 | ||
|
|
cfeb56e18a | ||
|
|
9652748ba9 | ||
|
|
91c4bfa81c | ||
|
|
e843297238 | ||
|
|
f5425f916a | ||
|
|
f6943296c7 | ||
|
|
6bf91a3956 | ||
|
|
4d2300f17e | ||
|
|
f449b8b454 | ||
|
|
9ef4c7fffd | ||
|
|
460cdf4508 | ||
|
|
2af9d002b6 | ||
|
|
7e12b60158 | ||
|
|
a2b13715ed | ||
|
|
f55264499c | ||
|
|
f3796d2eb8 | ||
|
|
ec335629b4 | ||
|
|
c36b577566 | ||
|
|
6f8f8e6a28 | ||
|
|
c536955b70 | ||
|
|
7d195deece | ||
|
|
7d85ce93bb | ||
|
|
7a96911a83 | ||
|
|
096d749411 | ||
|
|
623d985e7f | ||
|
|
68ab08aa56 | ||
|
|
ab8789739f | ||
|
|
60a8267576 | ||
|
|
5f0424fb11 | ||
|
|
393ff50b8d | ||
|
|
68a54abb09 | ||
|
|
8cecbcffb8 | ||
|
|
102628dc59 | ||
|
|
11374a2707 | ||
|
|
4483677cdd | ||
|
|
0e8d79bbfb | ||
|
|
c71f6e2188 | ||
|
|
371bca57aa | ||
|
|
f254675a5e | ||
|
|
066bef744f | ||
|
|
fbe3445c48 | ||
|
|
5b51c000af | ||
|
|
aaa33ad883 | ||
|
|
9be8bef575 | ||
|
|
0a6f43c1a1 | ||
|
|
6b59c2f44c | ||
|
|
fd0ab4a626 | ||
|
|
22d64368a6 | ||
|
|
79ca722b49 | ||
|
|
c3f1bd5a25 | ||
|
|
3ca386f223 | ||
|
|
00f4b54730 | ||
|
|
a8ab1fea58 | ||
|
|
8e57cee30f | ||
|
|
7f1c03a122 | ||
|
|
6fd4a02f95 | ||
|
|
531ee3a1be | ||
|
|
28b6e4dd5b | ||
|
|
f17b5f0f49 | ||
|
|
86a2f28561 | ||
|
|
0d9b1e73b8 | ||
|
|
fe0db7fe44 | ||
|
|
6ebc6c0354 | ||
|
|
a82f0ab657 | ||
|
|
0a6e22ba54 | ||
|
|
211774ccbc | ||
|
|
c41bcddd8d | ||
|
|
2a4cee6044 | ||
|
|
dba513c921 | ||
|
|
b4b3527df7 | ||
|
|
17a1a33987 | ||
|
|
4b9b189016 | ||
|
|
e1ecf94428 | ||
|
|
2da758ac86 | ||
|
|
efe6a38648 | ||
|
|
c3fc9e9815 | ||
|
|
43f5983613 | ||
|
|
723ba42264 | ||
|
|
081e9883e6 | ||
|
|
df311b60b4 | ||
|
|
0ff0dcecec | ||
|
|
eda09d2b32 | ||
|
|
340c8e8426 | ||
|
|
3777dc45d0 | ||
|
|
adf0a19f9d | ||
|
|
b748e269ec | ||
|
|
25b390b2ae | ||
|
|
52b7fd8c45 | ||
|
|
4268dd1777 | ||
|
|
24b7d080af | ||
|
|
39f0e4528a | ||
|
|
6b0bc0b3be | ||
|
|
2bf6d75e57 | ||
|
|
3dd6f9cb85 | ||
|
|
044d153c37 | ||
|
|
609c521c17 | ||
|
|
0b893d5634 | ||
|
|
aaf78297c9 | ||
|
|
64f8fe4987 | ||
|
|
e85a396572 | ||
|
|
99a16dcc1f | ||
|
|
8b6db21e56 | ||
|
|
01051016f5 | ||
|
|
c4a651bcac | ||
|
|
62c1ffcb52 | ||
|
|
00da3e3669 | ||
|
|
d4a5c81034 | ||
|
|
26f257a6ac | ||
|
|
12f3d2115d | ||
|
|
24e60747b6 | ||
|
|
5b6036525c | ||
|
|
bd4098191b | ||
|
|
df22c4d613 | ||
|
|
0561d73ae2 | ||
|
|
bf758809ba | ||
|
|
1fb4ca4525 | ||
|
|
e59646aafb | ||
|
|
bd8510f4da | ||
|
|
c56e171f3a | ||
|
|
812e76c39b | ||
|
|
8761819b24 | ||
|
|
47066e1b89 | ||
|
|
6a0a1031a1 | ||
|
|
33efcc6b93 | ||
|
|
2eddfd3ea8 | ||
|
|
bc8763ccf3 | ||
|
|
9f839f001f | ||
|
|
237318dfd3 | ||
|
|
5fe1905e4b | ||
|
|
f0ba8f6591 | ||
|
|
54b5ec206e | ||
|
|
befee75917 | ||
|
|
5ae126b10e | ||
|
|
c0b5c12dee | ||
|
|
214a8e1fed | ||
|
|
f89637bd8f | ||
|
|
bee3894cdf | ||
|
|
df76afb513 | ||
|
|
c266e90a77 | ||
|
|
b70468341e | ||
|
|
21b71e83dd | ||
|
|
ccba07a5c0 | ||
|
|
42553cdc44 | ||
|
|
8a7a545628 | ||
|
|
d37eb5d10b | ||
|
|
36837ae4b6 | ||
|
|
31270e2f52 | ||
|
|
3ccffdb77e | ||
|
|
56c97407d4 | ||
|
|
bf7a173e1e | ||
|
|
2077975b1c | ||
|
|
e4d05a7852 | ||
|
|
6fc99cd749 | ||
|
|
01b0b6b345 | ||
|
|
59082e0b5f | ||
|
|
9e072cb174 | ||
|
|
c0508aded7 | ||
|
|
9882341ecf | ||
|
|
3c2339ba33 | ||
|
|
457c6a9d80 | ||
|
|
1f840b1fbc | ||
|
|
694966b613 | ||
|
|
0a8fca7ec4 | ||
|
|
3e2e0ac91c | ||
|
|
b21e784826 | ||
|
|
dda365e002 | ||
|
|
653d3f4f5d | ||
|
|
3dcc106c59 | ||
|
|
248654a75e | ||
|
|
fdc9882709 | ||
|
|
b606a25684 | ||
|
|
1b88eaf817 | ||
|
|
dc8268b30c | ||
|
|
b80701589c | ||
|
|
d04791243c | ||
|
|
c5f861a522 | ||
|
|
b6d545b332 | ||
|
|
a35ed671e6 | ||
|
|
f0a2f7a675 | ||
|
|
4cb9944067 | ||
|
|
625f613128 | ||
|
|
e406e8f1f0 | ||
|
|
90bafee5eb | ||
|
|
ab77140454 | ||
|
|
d1ae13c208 | ||
|
|
e9604a49e1 | ||
|
|
645a72482b | ||
|
|
730377a843 | ||
|
|
f36e45cb10 | ||
|
|
ed72097862 | ||
|
|
0b95cdacb8 | ||
|
|
1912a8acf4 | ||
|
|
79774d2f07 | ||
|
|
fa8cb004a1 | ||
|
|
3449401ae2 | ||
|
|
02b2a8aab0 | ||
|
|
575851d45c | ||
|
|
060b3b8d0f | ||
|
|
a8a9631d4f | ||
|
|
2efc7ebb48 | ||
|
|
905fd3ae00 | ||
|
|
ee8c35a48e | ||
|
|
fb58214fcc | ||
|
|
4554798cec | ||
|
|
80e5227bcd | ||
|
|
65f51b7b83 | ||
|
|
72750b9882 | ||
|
|
27719565ae | ||
|
|
65030e2c73 | ||
|
|
b5d56e2f3b | ||
|
|
a63676ce93 | ||
|
|
01af40413e | ||
|
|
f6a4ba98de | ||
|
|
263792a4ab | ||
|
|
3380fdc119 | ||
|
|
959cd035ba | ||
|
|
e7e15652cf | ||
|
|
f57cc3a9c0 | ||
|
|
29da7d3d90 | ||
|
|
f99299ee39 | ||
|
|
ae47b7fa80 | ||
|
|
a2afe7c205 | ||
|
|
f32e258a18 | ||
|
|
665bcaebbb | ||
|
|
398f7d950f | ||
|
|
e6ad0d63a1 | ||
|
|
f0808d6882 | ||
|
|
a4fd7e9f7f | ||
|
|
7a978ff3f6 | ||
|
|
9b0930a289 | ||
|
|
959e7b1b01 | ||
|
|
40cc826586 | ||
|
|
6c593bab5a | ||
|
|
9c18d0e628 | ||
|
|
ecd3fcc0af | ||
|
|
38017d3cec | ||
|
|
d88568266f | ||
|
|
f43519a16e | ||
|
|
771d7f1724 | ||
|
|
0844aa8d68 | ||
|
|
d73c4288ae | ||
|
|
9c1a411c6e | ||
|
|
8eabd4ae89 | ||
|
|
524ab44160 | ||
|
|
ea8eaaff07 | ||
|
|
a6e4f4f9aa | ||
|
|
af72c1374a | ||
|
|
7c0ca9aad2 | ||
|
|
b43392bac8 | ||
|
|
c8bc0d6d7b | ||
|
|
c4b375c1fc | ||
|
|
1308a3231d | ||
|
|
2eefd60344 | ||
|
|
5472364265 | ||
|
|
26bf7f251d | ||
|
|
7c14a6a503 | ||
|
|
acc5f5c6d1 | ||
|
|
2cb7ec9432 | ||
|
|
8b6979ac18 | ||
|
|
34407957b9 | ||
|
|
44f5cf3020 | ||
|
|
54fcbf12b0 | ||
|
|
31f6cf9ab5 | ||
|
|
3417b569c0 | ||
|
|
8e20986335 | ||
|
|
385e1c6122 | ||
|
|
3e158c0321 | ||
|
|
c969c4c903 | ||
|
|
8533bc7f00 | ||
|
|
2adff375db | ||
|
|
d959e501ae | ||
|
|
886362ebf2 | ||
|
|
8e221ea597 | ||
|
|
432ea277a7 | ||
|
|
b520dda3ef | ||
|
|
90bfa2bff5 | ||
|
|
3fc6eed1ce | ||
|
|
fbbc670d1f | ||
|
|
f022bc1269 | ||
|
|
5aebc74f8c | ||
|
|
fba2bf0b2f | ||
|
|
55550f2023 | ||
|
|
401a347f6f | ||
|
|
0432a566dd | ||
|
|
4a82979792 | ||
|
|
3918dbed59 | ||
|
|
b80b480769 | ||
|
|
d0a824dde4 | ||
|
|
01da592f77 | ||
|
|
40d129cf54 | ||
|
|
8c66f520af | ||
|
|
64b391cc1b | ||
|
|
8a37395760 | ||
|
|
f5d00ddffb | ||
|
|
dafd567e68 | ||
|
|
d55ccf5312 | ||
|
|
871be4ad87 | ||
|
|
6f3c59e086 | ||
|
|
fc0d510e85 | ||
|
|
742a280f5d | ||
|
|
87126704cd | ||
|
|
1450a7fad4 | ||
|
|
65551159e0 | ||
|
|
d99dca2db3 | ||
|
|
3355399b4e | ||
|
|
dcadc61220 | ||
|
|
3c5027de3c | ||
|
|
a5d155a35a | ||
|
|
05bbd9ace3 | ||
|
|
559a8b2a2c | ||
|
|
c97e7e720e | ||
|
|
f10c47955f | ||
|
|
23db2b852b | ||
|
|
591093f867 | ||
|
|
9039b44f4d | ||
|
|
5f0c86a49f | ||
|
|
8ac7bda52c | ||
|
|
0e2e867e4a | ||
|
|
0d9ccba566 | ||
|
|
30e8071d1d | ||
|
|
39cbf4e9ab | ||
|
|
af47bd5da8 | ||
|
|
0ee5c97b1c | ||
|
|
d07f13eace | ||
|
|
fc3ca33033 | ||
|
|
2fb022e501 | ||
|
|
b478472b35 | ||
|
|
8fdadcc596 | ||
|
|
ca06043b00 | ||
|
|
3c9d3c2c04 | ||
|
|
998227ac53 | ||
|
|
30a066db41 | ||
|
|
d9f79fae79 | ||
|
|
530a710dce | ||
|
|
f5ca4840d6 | ||
|
|
d3a6b6f587 | ||
|
|
f90e0fc14c | ||
|
|
6b968796ce | ||
|
|
1277f8bb3b | ||
|
|
63a6ba53df | ||
|
|
9af51fb664 | ||
|
|
c2012a595b | ||
|
|
2fcfa5cf71 | ||
|
|
e638391182 | ||
|
|
e98fcfe56e | ||
|
|
2f366b0d5d | ||
|
|
e1a0e581b9 | ||
|
|
c43d8b3d85 | ||
|
|
6a2a121d4e | ||
|
|
9dc5709ce7 | ||
|
|
ccb09fbf45 | ||
|
|
fb883254f6 | ||
|
|
5f1f3dff5e | ||
|
|
dd93de75d9 | ||
|
|
d897159560 | ||
|
|
182c975e01 | ||
|
|
f19a324030 | ||
|
|
9cf965a157 | ||
|
|
9e42be09d7 | ||
|
|
7d466be94e | ||
|
|
a5c0cbc3b4 | ||
|
|
bb5dc4c473 | ||
|
|
132386f64d | ||
|
|
b0c89d739b | ||
|
|
6a07a7ed2c | ||
|
|
f293ab86c3 | ||
|
|
db60abc604 | ||
|
|
f55c93a1e4 | ||
|
|
08296bd3ce | ||
|
|
fca6da3a5a | ||
|
|
49407c2e4f | ||
|
|
4d1474afe0 | ||
|
|
aa128d6c07 | ||
|
|
c8c76cc03f | ||
|
|
5a9bfea419 | ||
|
|
dffe70e40a | ||
|
|
1b4d9a6147 | ||
|
|
b7fb3ec9a9 | ||
|
|
7ff18ca14b | ||
|
|
6052b1546d | ||
|
|
5a241392cf | ||
|
|
c586c63533 | ||
|
|
f8919f8baa | ||
|
|
f45ed1613e | ||
|
|
bc665fdbc3 | ||
|
|
6c3e6bc90c | ||
|
|
cb3b7f3cb2 | ||
|
|
5e8db32329 | ||
|
|
65c39763eb | ||
|
|
f429469fc8 | ||
|
|
6339b62f8d | ||
|
|
473e9ab55c | ||
|
|
77e503f5ca | ||
|
|
c64ecb1ab8 | ||
|
|
2e22dcfc47 | ||
|
|
16fd6558a6 | ||
|
|
eb97151476 | ||
|
|
b8adee4004 | ||
|
|
65f5eacdd4 | ||
|
|
2b6597e9f1 | ||
|
|
6a2f73df9c | ||
|
|
60a03d0293 | ||
|
|
dd6735285d | ||
|
|
dbf5c395b0 | ||
|
|
8261ffeeeb | ||
|
|
1e438cdc45 | ||
|
|
d70859ef1b | ||
|
|
3ab8ff2ea1 | ||
|
|
adea414695 | ||
|
|
cace03c3cc | ||
|
|
d55b1d7259 | ||
|
|
78e963688c | ||
|
|
b2df872ad4 | ||
|
|
fea4201f46 | ||
|
|
073a2f4d65 | ||
|
|
06df678d33 | ||
|
|
dd65c231f9 | ||
|
|
ae690e0679 | ||
|
|
06f108df3b | ||
|
|
a8319d1131 | ||
|
|
f800290d89 | ||
|
|
e79de47f5e | ||
|
|
08c22416a2 | ||
|
|
dac5d313b3 | ||
|
|
21d139c6c2 | ||
|
|
e5d997a6c0 | ||
|
|
a2e54dc94f | ||
|
|
28040109ac | ||
|
|
39b6bf62ba | ||
|
|
6a19a8fb7e | ||
|
|
e914b6fe8d | ||
|
|
e5a6dbd77a | ||
|
|
343dc05f82 | ||
|
|
76076cdb3c | ||
|
|
487539542a | ||
|
|
1634bef28a | ||
|
|
6882f7a9c0 | ||
|
|
943c8507f7 | ||
|
|
99119562bb | ||
|
|
5cbe595fe3 | ||
|
|
eb8257a52e | ||
|
|
27d8afe4a7 | ||
|
|
3391ec5377 | ||
|
|
88f21eae87 | ||
|
|
1258923a0d | ||
|
|
344286e037 | ||
|
|
3ced81a2c2 | ||
|
|
032c48fb25 | ||
|
|
f6125a2542 | ||
|
|
49a03a86fb | ||
|
|
e84acd9898 | ||
|
|
8022a74565 | ||
|
|
fdc6fc59b8 | ||
|
|
f3340b113f | ||
|
|
20f24bffc4 | ||
|
|
7ebcc69cb9 | ||
|
|
90e5c7f3eb | ||
|
|
81918713dd | ||
|
|
ddceaaf3d5 | ||
|
|
824b900a43 |
No files matched your search
+10
-1
@@ -8,6 +8,11 @@
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
# reverting this as e.g. /distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/
|
||||
# should not be ignored
|
||||
#bin/
|
||||
.factorypath
|
||||
|
||||
|
||||
# NetBeans #
|
||||
############
|
||||
@@ -54,4 +59,8 @@ target
|
||||
##########
|
||||
# KEYCLOAK-5391: We will re-exclude node_modules when node_modules handling is worked out.
|
||||
# For now, we keep our js libraries checked into GitHub, so we don't ignore.
|
||||
#node_modules
|
||||
#node_modules
|
||||
|
||||
# testsuite #
|
||||
#############
|
||||
*offline-token.txt
|
||||
+7
-2
@@ -1,5 +1,4 @@
|
||||
language: java
|
||||
dist: precise
|
||||
|
||||
cache:
|
||||
cache: false
|
||||
@@ -14,8 +13,11 @@ env:
|
||||
- TESTS=server-group2
|
||||
- TESTS=server-group3
|
||||
- TESTS=server-group4
|
||||
- TESTS=adapter-tests
|
||||
- TESTS=old
|
||||
- TESTS=crossdc
|
||||
- TESTS=crossdc-server
|
||||
- TESTS=crossdc-adapter
|
||||
- TESTS=ssl
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
@@ -35,4 +37,7 @@ before_install:
|
||||
script:
|
||||
- ./travis-run-tests.sh $TESTS
|
||||
|
||||
after_success:
|
||||
- .travis/docker-hub-master-trigger.sh
|
||||
|
||||
sudo: false
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
|
||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "master"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
|
||||
echo "Triggered Docker hub build"
|
||||
elif [ "$DOCKER_HUB_TOKEN" != "" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_REPO_SLUG" == "keycloak/keycloak" ] && [ "$TRAVIS_BRANCH" == "openshift-integration2" ]; then
|
||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "nightly-openshift-integration2"}' -X POST https://registry.hub.docker.com/u/jboss/keycloak/trigger/$DOCKER_HUB_TOKEN/
|
||||
echo "Triggered Docker hub build"
|
||||
fi
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
ADOPTERS
|
||||
========
|
||||
|
||||
PUBLIC REFERENCES
|
||||
-----------------
|
||||
|
||||
List of organization names below is based on information collected using Keycloak Community Survey. It contains company names from participants who agreed to serve as public reference. If organizations would like get added or removed please make a PR.
|
||||
|
||||
* Accenture
|
||||
* Actinver
|
||||
* Akvo Foundation
|
||||
* Appier
|
||||
* BISPRO
|
||||
* Bluestem Brands, Inc
|
||||
* Bundesversicherungsamt
|
||||
* Capgemini
|
||||
* Chassi
|
||||
* CloudNative Inc.
|
||||
* Copenhagen Optimization
|
||||
* Curecomp GmbH
|
||||
* Cybertech
|
||||
* Devsu
|
||||
* Devoxx / Voxxed
|
||||
* DUENE e.V - project
|
||||
* DukeCon
|
||||
* European Synchrotron Radiation Facility
|
||||
* Fluance AG
|
||||
* Hewlett-Packard Enterprise
|
||||
* Hitachi
|
||||
* INEAT
|
||||
* Inventage
|
||||
* ISAAC
|
||||
* ITROI Solutions
|
||||
* Kindly Ops, LLC
|
||||
* msg systems ag
|
||||
* Netdava International
|
||||
* Ohio Supercomputer Center
|
||||
* Okta
|
||||
* PharmaPartners B.V.
|
||||
* Plivo
|
||||
* Price Insight
|
||||
* Prodesan
|
||||
* Quest Software
|
||||
* Research Industrial Software Engineering (RISE)
|
||||
* Sportsbet.com.au
|
||||
* Stack Labs
|
||||
* Storebrand
|
||||
* Synekus
|
||||
* Synetek Solutions
|
||||
* Taklane
|
||||
* TrackingSport
|
||||
* TRT9 - Brasil
|
||||
* UnitedHealthcare
|
||||
* ...More individuals
|
||||
@@ -32,6 +32,10 @@ To build the distribution run:
|
||||
|
||||
Once completed you will find distribution archives in `distribution`.
|
||||
|
||||
To build only the server run:
|
||||
|
||||
mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install
|
||||
|
||||
|
||||
Starting Keycloak
|
||||
-----------------
|
||||
@@ -40,6 +44,7 @@ To start Keycloak during development first build as specified above, then run:
|
||||
|
||||
mvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server
|
||||
|
||||
When running testsuite, by default an account with username `admin` and password `admin` will be created within the master realm at start.
|
||||
|
||||
To start Keycloak from the server distribution first build the distribution it as specified above, then run:
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -31,12 +31,24 @@
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.*
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
org.keycloak.*;version="${project.version}",
|
||||
org.apache.http.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.auth.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.client.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.conn.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.cookie.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.impl.auth.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.impl.client.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.impl.conn.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.impl.cookie.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.impl.execchain.*;version=${apache.httpcomponents.version},
|
||||
org.apache.http.*;version=${apache.httpcomponents.httpcore.version},
|
||||
org.apache.karaf.jaas.boot.principal;resolution:=optional,
|
||||
org.apache.karaf.jaas.modules;resolution:=optional,
|
||||
*;resolution:=optional
|
||||
@@ -52,7 +64,6 @@
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -98,15 +109,6 @@
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
|
||||
+20
@@ -307,6 +307,16 @@ public class AdapterDeploymentContext {
|
||||
delegate.setTokenStore(tokenStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdapterStateCookiePath() {
|
||||
return delegate.getAdapterStateCookiePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapterStateCookiePath(String adapterStateCookiePath) {
|
||||
delegate.setAdapterStateCookiePath(adapterStateCookiePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStateCookieName() {
|
||||
return delegate.getStateCookieName();
|
||||
@@ -482,6 +492,16 @@ public class AdapterDeploymentContext {
|
||||
public void setPublicKeyCacheTtl(int publicKeyCacheTtl) {
|
||||
delegate.setPublicKeyCacheTtl(publicKeyCacheTtl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerifyTokenAudience() {
|
||||
return delegate.isVerifyTokenAudience();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVerifyTokenAudience(boolean verifyTokenAudience) {
|
||||
delegate.setVerifyTokenAudience(verifyTokenAudience);
|
||||
}
|
||||
}
|
||||
|
||||
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {
|
||||
|
||||
+3
@@ -106,7 +106,10 @@ public class AuthenticatedActionsHandler {
|
||||
if (deployment.getPolicyEnforcer() != null) {
|
||||
if (exposeHeaders != null) {
|
||||
exposeHeaders += ",";
|
||||
} else {
|
||||
exposeHeaders = "";
|
||||
}
|
||||
|
||||
exposeHeaders += "WWW-Authenticate";
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
||||
return authenticateToken(exchange, atr.getToken());
|
||||
}
|
||||
|
||||
private AccessTokenResponse getToken(String username, String password) throws Exception {
|
||||
protected AccessTokenResponse getToken(String username, String password) throws Exception {
|
||||
AccessTokenResponse tokenResponse=null;
|
||||
HttpClient client = deployment.getClient();
|
||||
|
||||
|
||||
+8
-4
@@ -18,7 +18,7 @@
|
||||
package org.keycloak.adapters;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
@@ -72,8 +72,12 @@ public class BearerTokenRequestAuthenticator {
|
||||
for (String authHeader : authHeaders) {
|
||||
String[] split = authHeader.trim().split("\\s+");
|
||||
if (split == null || split.length != 2) continue;
|
||||
if (!split[0].equalsIgnoreCase("Bearer")) continue;
|
||||
tokenString = split[1];
|
||||
if (split[0].equalsIgnoreCase("Bearer")) {
|
||||
tokenString = split[1];
|
||||
|
||||
log.debugf("Found [%d] values in authorization header, selecting the first value for Bearer.", (Integer) authHeaders.size());
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
if (tokenString == null) {
|
||||
@@ -96,7 +100,7 @@ public class BearerTokenRequestAuthenticator {
|
||||
}
|
||||
}
|
||||
try {
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
token = AdapterTokenVerifier.verifyToken(tokenString, deployment);
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify token", e);
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
|
||||
|
||||
+27
-6
@@ -19,7 +19,8 @@ package org.keycloak.adapters;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
@@ -46,7 +47,7 @@ public class CookieTokenStore {
|
||||
.append(idToken).append(DELIM)
|
||||
.append(refreshToken).toString();
|
||||
|
||||
String cookiePath = getContextPath(facade);
|
||||
String cookiePath = getCookiePath(deployment, facade);
|
||||
facade.getResponse().setCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookie, cookiePath, null, -1, deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr()), true);
|
||||
}
|
||||
|
||||
@@ -71,7 +72,11 @@ public class CookieTokenStore {
|
||||
|
||||
try {
|
||||
// Skip check if token is active now. It's supposed to be done later by the caller
|
||||
AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
|
||||
TokenVerifier<AccessToken> tokenVerifier = AdapterTokenVerifier.createVerifier(accessTokenString, deployment, true, AccessToken.class)
|
||||
.checkActive(false)
|
||||
.verify();
|
||||
AccessToken accessToken = tokenVerifier.getToken();
|
||||
|
||||
IDToken idToken;
|
||||
if (idTokenString != null && idTokenString.length() > 0) {
|
||||
try {
|
||||
@@ -93,14 +98,30 @@ public class CookieTokenStore {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeCookie(HttpFacade facade) {
|
||||
String cookiePath = getContextPath(facade);
|
||||
public static void removeCookie(KeycloakDeployment deployment, HttpFacade facade) {
|
||||
String cookiePath = getCookiePath(deployment, facade);
|
||||
facade.getResponse().resetCookie(AdapterConstants.KEYCLOAK_ADAPTER_STATE_COOKIE, cookiePath);
|
||||
}
|
||||
|
||||
private static String getContextPath(HttpFacade facade) {
|
||||
static String getCookiePath(KeycloakDeployment deployment, HttpFacade facade) {
|
||||
String path = deployment.getAdapterStateCookiePath() == null ? "" : deployment.getAdapterStateCookiePath().trim();
|
||||
if (path.startsWith("/")) {
|
||||
return path;
|
||||
}
|
||||
String contextPath = getContextPath(facade);
|
||||
StringBuilder cookiePath = new StringBuilder(contextPath);
|
||||
if (!contextPath.endsWith("/") && !path.isEmpty()) {
|
||||
cookiePath.append("/");
|
||||
}
|
||||
return cookiePath.append(path).toString();
|
||||
}
|
||||
|
||||
static String getContextPath(HttpFacade facade) {
|
||||
String uri = facade.getRequest().getURI();
|
||||
String path = KeycloakUriBuilder.fromUri(uri).getPath();
|
||||
if (path == null || path.isEmpty()) {
|
||||
return "/";
|
||||
}
|
||||
int index = path.indexOf("/", 1);
|
||||
return index == -1 ? path : path.substring(0, index);
|
||||
}
|
||||
|
||||
+19
-1
@@ -70,6 +70,7 @@ public class KeycloakDeployment {
|
||||
protected SslRequired sslRequired = SslRequired.ALL;
|
||||
protected int confidentialPort = -1;
|
||||
protected TokenStore tokenStore = TokenStore.SESSION;
|
||||
protected String adapterStateCookiePath = "";
|
||||
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||
protected boolean useResourceRoleMappings;
|
||||
protected boolean cors;
|
||||
@@ -92,10 +93,11 @@ public class KeycloakDeployment {
|
||||
// https://tools.ietf.org/html/rfc7636
|
||||
protected boolean pkce = false;
|
||||
protected boolean ignoreOAuthQueryParameter;
|
||||
|
||||
|
||||
protected Map<String, String> redirectRewriteRules;
|
||||
|
||||
protected boolean delegateBearerErrorResponseSending = false;
|
||||
protected boolean verifyTokenAudience = false;
|
||||
|
||||
public KeycloakDeployment() {
|
||||
}
|
||||
@@ -296,6 +298,14 @@ public class KeycloakDeployment {
|
||||
this.tokenStore = tokenStore;
|
||||
}
|
||||
|
||||
public String getAdapterStateCookiePath() {
|
||||
return adapterStateCookiePath;
|
||||
}
|
||||
|
||||
public void setAdapterStateCookiePath(String adapterStateCookiePath) {
|
||||
this.adapterStateCookiePath = adapterStateCookiePath;
|
||||
}
|
||||
|
||||
public String getStateCookieName() {
|
||||
return stateCookieName;
|
||||
}
|
||||
@@ -477,4 +487,12 @@ public class KeycloakDeployment {
|
||||
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
|
||||
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
|
||||
}
|
||||
|
||||
public boolean isVerifyTokenAudience() {
|
||||
return verifyTokenAudience;
|
||||
}
|
||||
|
||||
public void setVerifyTokenAudience(boolean verifyTokenAudience) {
|
||||
this.verifyTokenAudience = verifyTokenAudience;
|
||||
}
|
||||
}
|
||||
+4
@@ -88,6 +88,9 @@ public class KeycloakDeploymentBuilder {
|
||||
} else {
|
||||
deployment.setTokenStore(TokenStore.SESSION);
|
||||
}
|
||||
if (adapterConfig.getTokenCookiePath() != null) {
|
||||
deployment.setAdapterStateCookiePath(adapterConfig.getTokenCookiePath());
|
||||
}
|
||||
if (adapterConfig.getPrincipalAttribute() != null) deployment.setPrincipalAttribute(adapterConfig.getPrincipalAttribute());
|
||||
|
||||
deployment.setResourceCredentials(adapterConfig.getCredentials());
|
||||
@@ -122,6 +125,7 @@ public class KeycloakDeploymentBuilder {
|
||||
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
|
||||
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
|
||||
deployment.setRewriteRedirectRules(adapterConfig.getRedirectRewriteRules());
|
||||
deployment.setVerifyTokenAudience(adapterConfig.isVerifyTokenAudience());
|
||||
|
||||
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
|
||||
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
|
||||
|
||||
+4
-11
@@ -19,7 +19,7 @@ package org.keycloak.adapters;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.adapters.spi.AdapterSessionStore;
|
||||
import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
@@ -40,7 +40,6 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
/**
|
||||
@@ -359,15 +358,9 @@ public class OAuthRequestAuthenticator {
|
||||
}
|
||||
|
||||
try {
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
if (idTokenString != null) {
|
||||
try {
|
||||
JWSInput input = new JWSInput(idTokenString);
|
||||
idToken = input.readJsonContent(IDToken.class);
|
||||
} catch (JWSInputException e) {
|
||||
throw new VerificationException();
|
||||
}
|
||||
}
|
||||
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, idTokenString, deployment);
|
||||
token = tokens.getAccessToken();
|
||||
idToken = tokens.getIdToken();
|
||||
log.debug("Token Verification succeeded!");
|
||||
} catch (VerificationException e) {
|
||||
log.error("failed verification of token: " + e.getMessage());
|
||||
|
||||
+24
-16
@@ -20,30 +20,27 @@ package org.keycloak.adapters;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.adapters.authentication.ClientCredentialsProvider;
|
||||
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.UserSessionManagement;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.jose.jwk.JSONWebKeySet;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jwk.JWKBuilder;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.representations.VersionRepresentation;
|
||||
import org.keycloak.constants.AdapterConstants;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.representations.VersionRepresentation;
|
||||
import org.keycloak.representations.adapters.action.AdminAction;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
|
||||
import org.keycloak.representations.adapters.action.TestAvailabilityAction;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
@@ -213,17 +210,19 @@ public class PreAuthActionsHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
JWSInput input = new JWSInput(token);
|
||||
PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input.getHeader().getKeyId(), deployment);
|
||||
if (RSAProvider.verify(input, publicKey)) {
|
||||
return input;
|
||||
// Check just signature. Other things checked in validateAction
|
||||
TokenVerifier tokenVerifier = AdapterTokenVerifier.createVerifier(token, deployment, false, JsonWebToken.class);
|
||||
tokenVerifier.verify();
|
||||
return new JWSInput(token);
|
||||
} catch (VerificationException ignore) {
|
||||
log.warn("admin request failed, unable to verify token: " + ignore.getMessage());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(ignore.getMessage(), ignore);
|
||||
}
|
||||
} catch (JWSInputException ignore) {
|
||||
}
|
||||
|
||||
log.warn("admin request failed, unable to verify token");
|
||||
facade.getResponse().sendError(403, "no token");
|
||||
return null;
|
||||
facade.getResponse().sendError(403, "token failed verification");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +249,15 @@ public class PreAuthActionsHandler {
|
||||
protected void handleVersion() {
|
||||
try {
|
||||
facade.getResponse().setStatus(200);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
if (deployment.isCors()) {
|
||||
String origin = facade.getRequest().getHeader(CorsHeaders.ORIGIN);
|
||||
if (origin == null) {
|
||||
log.debug("no origin header set in request");
|
||||
} else {
|
||||
facade.getResponse().setHeader(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
}
|
||||
}
|
||||
facade.getResponse().setHeader("Content-Type", "application/json");
|
||||
JsonSerialization.writeValueToStream(facade.getResponse().getOutputStream(), VersionRepresentation.SINGLETON);
|
||||
} catch (Exception e) {
|
||||
|
||||
+3
-2
@@ -20,7 +20,7 @@ package org.keycloak.adapters;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
@@ -130,7 +130,8 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||
String tokenString = response.getToken();
|
||||
AccessToken token = null;
|
||||
try {
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, response.getIdToken(), deployment);
|
||||
token = tokens.getAccessToken();
|
||||
log.debug("Token Verification succeeded!");
|
||||
} catch (VerificationException e) {
|
||||
log.error("failed verification of token");
|
||||
|
||||
+57
-30
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.keycloak.adapters.authorization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -28,6 +30,7 @@ import org.jboss.logging.Logger;
|
||||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade.Request;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
@@ -66,8 +69,16 @@ public abstract class AbstractPolicyEnforcer {
|
||||
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
|
||||
|
||||
if (securityContext == null) {
|
||||
if (pathConfig != null) {
|
||||
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
|
||||
if (!isDefaultAccessDeniedUri(request)) {
|
||||
if (pathConfig != null) {
|
||||
if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
|
||||
return createEmptyAuthorizationContext(true);
|
||||
} else {
|
||||
challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
|
||||
}
|
||||
} else {
|
||||
handleAccessDenied(httpFacade);
|
||||
}
|
||||
}
|
||||
return createEmptyAuthorizationContext(false);
|
||||
}
|
||||
@@ -102,8 +113,9 @@ public abstract class AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
|
||||
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
|
||||
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
|
||||
try {
|
||||
return createAuthorizationContext(accessToken, pathConfig);
|
||||
} catch (Exception e) {
|
||||
@@ -132,7 +144,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
|
||||
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
|
||||
|
||||
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
|
||||
Request request = httpFacade.getRequest();
|
||||
|
||||
if (isDefaultAccessDeniedUri(request)) {
|
||||
@@ -146,7 +158,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
boolean hasPermission = false;
|
||||
List<Permission> grantedPermissions = authorization.getPermissions();
|
||||
Collection<Permission> grantedPermissions = authorization.getPermissions();
|
||||
|
||||
for (Permission permission : grantedPermissions) {
|
||||
if (permission.getResourceId() != null) {
|
||||
@@ -165,7 +177,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
|
||||
}
|
||||
|
||||
return hasValidClaims(actualPathConfig, httpFacade, authorization);
|
||||
return hasValidClaims(permission, claims);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -187,35 +199,20 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasValidClaims(PathConfig actualPathConfig, OIDCHttpFacade httpFacade, Authorization authorization) {
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = actualPathConfig.getClaimInformationPointConfig();
|
||||
private boolean hasValidClaims(Permission permission, Map<String, List<String>> claims) {
|
||||
Map<String, Set<String>> grantedClaims = permission.getClaims();
|
||||
|
||||
if (claimInformationPointConfig != null) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
|
||||
for (Entry<String, Map<String, Object>> entry : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = policyEnforcer.getClaimInformationPointProviderFactories().get(entry.getKey());
|
||||
|
||||
if (factory == null) {
|
||||
throw new RuntimeException("Could not find claim information provider with name [" + entry.getKey() + "]");
|
||||
}
|
||||
|
||||
claims.putAll(factory.create(entry.getValue()).resolve(httpFacade));
|
||||
if (grantedClaims != null) {
|
||||
if (claims.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, List<String>> grantedClaims = authorization.getClaims();
|
||||
for (Entry<String, Set<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
|
||||
if (grantedClaims != null) {
|
||||
if (claims.isEmpty()) {
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
return false;
|
||||
}
|
||||
for (Entry<String, List<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +310,15 @@ public abstract class AbstractPolicyEnforcer {
|
||||
MethodConfig methodConfig = new MethodConfig();
|
||||
|
||||
methodConfig.setMethod(request.getMethod());
|
||||
methodConfig.setScopes(pathConfig.getScopes());
|
||||
List scopes = new ArrayList<>();
|
||||
|
||||
if (Boolean.TRUE.equals(getEnforcerConfig().getHttpMethodAsScope())) {
|
||||
scopes.add(request.getMethod());
|
||||
} else {
|
||||
scopes.addAll(pathConfig.getScopes());
|
||||
}
|
||||
|
||||
methodConfig.setScopes(scopes);
|
||||
methodConfig.setScopesEnforcementMode(PolicyEnforcerConfig.ScopeEnforcementMode.ANY);
|
||||
|
||||
return methodConfig;
|
||||
@@ -342,4 +347,26 @@ public abstract class AbstractPolicyEnforcer {
|
||||
private PathConfig getPathConfig(Request request) {
|
||||
return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
|
||||
|
||||
claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
|
||||
if (claimInformationPointConfig != null) {
|
||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||
|
||||
if (factory != null) {
|
||||
return factory.create(claimDef.getValue()).resolve(httpFacade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
+36
-73
@@ -18,17 +18,16 @@
|
||||
package org.keycloak.adapters.authorization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.authorization.client.AuthorizationDeniedException;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
@@ -56,14 +55,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
|
||||
AccessToken original = accessToken;
|
||||
|
||||
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
|
||||
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
|
||||
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade, claims);
|
||||
|
||||
if (accessToken == null) {
|
||||
return false;
|
||||
@@ -79,32 +78,19 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
|
||||
|
||||
if (newAuthorization != null) {
|
||||
List<Permission> grantedPermissions = authorization.getPermissions();
|
||||
List<Permission> newPermissions = newAuthorization.getPermissions();
|
||||
Collection<Permission> grantedPermissions = authorization.getPermissions();
|
||||
Collection<Permission> newPermissions = newAuthorization.getPermissions();
|
||||
|
||||
for (Permission newPermission : newPermissions) {
|
||||
if (!grantedPermissions.contains(newPermission)) {
|
||||
grantedPermissions.add(newPermission);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<String>> newClaims = newAuthorization.getClaims();
|
||||
|
||||
if (newClaims != null) {
|
||||
Map<String, List<String>> claims = authorization.getClaims();
|
||||
|
||||
if (claims == null) {
|
||||
claims = new HashMap<>();
|
||||
authorization.setClaims(claims);
|
||||
}
|
||||
|
||||
claims.putAll(newClaims);
|
||||
}
|
||||
}
|
||||
|
||||
original.setAuthorization(authorization);
|
||||
|
||||
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
|
||||
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade, claims);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,12 +122,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
|
||||
@Override
|
||||
protected void handleAccessDenied(OIDCHttpFacade facade) {
|
||||
KeycloakSecurityContext securityContext = facade.getSecurityContext();
|
||||
|
||||
if (securityContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
|
||||
HttpFacade.Response response = facade.getResponse();
|
||||
|
||||
@@ -153,8 +133,8 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
}
|
||||
|
||||
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
|
||||
if (getPolicyEnforcer().getDeployment().isBearerOnly() || (isBearerAuthorization(httpFacade) && getEnforcerConfig().getUserManagedAccess() != null)) {
|
||||
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade, Map<String, List<String>> claims) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -165,20 +145,13 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
AccessToken accessToken = securityContext.getToken();
|
||||
AuthorizationRequest authzRequest = new AuthorizationRequest();
|
||||
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
String ticket = getPermissionTicket(pathConfig, methodConfig, getAuthzClient(), httpFacade);
|
||||
authzRequest.setTicket(ticket);
|
||||
} else {
|
||||
if (accessToken.getAuthorization() != null) {
|
||||
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
|
||||
}
|
||||
if (isBearerAuthorization(httpFacade) || accessToken.getAuthorization() != null) {
|
||||
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
|
||||
}
|
||||
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
|
||||
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
|
||||
}
|
||||
if (!claims.isEmpty()) {
|
||||
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
|
||||
authzRequest.setClaimToken(Base64.encodeBytes(JsonSerialization.writeValueAsBytes(claims)));
|
||||
}
|
||||
|
||||
if (accessToken.getAuthorization() != null) {
|
||||
@@ -186,10 +159,17 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
LOGGER.debug("Obtaining authorization for authenticated user.");
|
||||
AuthorizationResponse authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
AuthorizationResponse authzResponse;
|
||||
|
||||
if (isBearerAuthorization(httpFacade)) {
|
||||
authzRequest.setSubjectToken(accessTokenString);
|
||||
authzResponse = getAuthzClient().authorization().authorize(authzRequest);
|
||||
} else {
|
||||
authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
}
|
||||
|
||||
if (authzResponse != null) {
|
||||
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
|
||||
return AdapterTokenVerifier.verifyToken(authzResponse.getToken(), deployment);
|
||||
}
|
||||
} catch (AuthorizationDeniedException ignore) {
|
||||
LOGGER.debug("Authorization denied", ignore);
|
||||
@@ -200,7 +180,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, HttpFacade httpFacade) {
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, OIDCHttpFacade httpFacade) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
ProtectionResource protection = authzClient.protection();
|
||||
PermissionResource permission = protection.permission();
|
||||
@@ -209,7 +189,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
permissionRequest.setResourceId(pathConfig.getId());
|
||||
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
|
||||
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
permissionRequest.setClaims(claims);
|
||||
@@ -221,35 +201,18 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getClaims(PathConfig pathConfig, HttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = pathConfig.getClaimInformationPointConfig();
|
||||
|
||||
if (claimInformationPointConfig != null) {
|
||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||
|
||||
if (factory != null) {
|
||||
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
|
||||
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
|
||||
if (authHeaders == null || authHeaders.size() == 0) {
|
||||
return false;
|
||||
|
||||
if (authHeaders != null) {
|
||||
for (String authHeader : authHeaders) {
|
||||
String[] split = authHeader.trim().split("\\s+");
|
||||
if (split == null || split.length != 2) continue;
|
||||
if (!split[0].equalsIgnoreCase("Bearer")) continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (String authHeader : authHeaders) {
|
||||
String[] split = authHeader.trim().split("\\s+");
|
||||
if (split == null || split.length != 2) continue;
|
||||
if (!split[0].equalsIgnoreCase("Bearer")) continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return getPolicyEnforcer().getDeployment().isBearerOnly();
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -21,6 +21,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
|
||||
/**
|
||||
@@ -154,7 +155,7 @@ public class PathCache {
|
||||
if(maxAge == -1) {
|
||||
expiration = -1;
|
||||
} else {
|
||||
expiration = System.currentTimeMillis() + maxAge;
|
||||
expiration = Time.currentTimeMillis() + maxAge;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +168,7 @@ public class PathCache {
|
||||
}
|
||||
|
||||
boolean isExpired() {
|
||||
return expiration != -1 ? System.currentTimeMillis() > expiration : false;
|
||||
return expiration != -1 ? Time.currentTimeMillis() > expiration : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-5
@@ -225,9 +225,10 @@ public class PolicyEnforcer {
|
||||
for (String id : protectedResource.findAll()) {
|
||||
ResourceRepresentation resourceDescription = protectedResource.findById(id);
|
||||
|
||||
if (resourceDescription.getUri() != null) {
|
||||
PathConfig pathConfig = PathConfig.createPathConfig(resourceDescription);
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
if (resourceDescription.getUris() != null && !resourceDescription.getUris().isEmpty()) {
|
||||
for(PathConfig pathConfig : PathConfig.createPathConfigs(resourceDescription)) {
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,7 +272,17 @@ public class PolicyEnforcer {
|
||||
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
|
||||
|
||||
if (!matchingResources.isEmpty()) {
|
||||
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
|
||||
Map<String, Map<String, Object>> cipConfig = null;
|
||||
|
||||
if (pathConfig != null) {
|
||||
cipConfig = pathConfig.getClaimInformationPointConfig();
|
||||
}
|
||||
|
||||
pathConfig = PathConfig.createPathConfigs(matchingResources.get(0)).iterator().next();
|
||||
|
||||
if (cipConfig != null) {
|
||||
pathConfig.setClaimInformationPointConfig(cipConfig);
|
||||
}
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
LOGGER.errorf(cause, "Could not lazy load resource with path [" + targetUri + "] from server");
|
||||
@@ -299,16 +310,27 @@ public class PolicyEnforcer {
|
||||
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
|
||||
if (originalConfig.hasPattern()) {
|
||||
ProtectedResource resource = authzClient.protection().resource();
|
||||
|
||||
// search by an exact match
|
||||
List<ResourceRepresentation> search = resource.findByUri(path);
|
||||
|
||||
// if exact match not found, try to obtain from current path the parent path.
|
||||
// if path is /resource/1/test and pattern from pathConfig is /resource/{id}/*, parent path is /resource/1
|
||||
// this logic allows to match sub resources of a resource instance (/resource/1) to the parent resource,
|
||||
// so any permission granted to parent also applies to sub resources
|
||||
if (search.isEmpty()) {
|
||||
search = resource.findByUri(buildUriFromTemplate(originalConfig.getPath(), path, true));
|
||||
}
|
||||
|
||||
if (!search.isEmpty()) {
|
||||
ResourceRepresentation targetResource = search.get(0);
|
||||
PathConfig config = PathConfig.createPathConfig(targetResource);
|
||||
PathConfig config = PathConfig.createPathConfigs(targetResource).iterator().next();
|
||||
|
||||
config.setScopes(originalConfig.getScopes());
|
||||
config.setMethods(originalConfig.getMethods());
|
||||
config.setParentConfig(originalConfig);
|
||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
||||
config.setClaimInformationPointConfig(originalConfig.getClaimInformationPointConfig());
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
+10
-2
@@ -23,7 +23,7 @@ import org.keycloak.adapters.AdapterUtils;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.FindFile;
|
||||
import org.keycloak.common.util.reflections.Reflections;
|
||||
@@ -202,8 +202,16 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
|
||||
|
||||
|
||||
protected Auth bearerAuth(String tokenString) throws VerificationException {
|
||||
AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
AccessToken token = AdapterTokenVerifier.verifyToken(tokenString, deployment);
|
||||
return postTokenVerification(tokenString, token);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called after accessToken was verified (including signature, expiration etc)
|
||||
*
|
||||
*/
|
||||
protected Auth postTokenVerification(String tokenString, AccessToken token) {
|
||||
boolean verifyCaller;
|
||||
if (deployment.isUseResourceRoleMappings()) {
|
||||
verifyCaller = token.isVerifyCaller(deployment.getResourceName());
|
||||
|
||||
+11
-1
@@ -27,6 +27,7 @@ import org.apache.http.message.BasicNameValuePair;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.constants.ServiceUrlConstants;
|
||||
@@ -56,11 +57,15 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
|
||||
|
||||
private static final Logger log = Logger.getLogger(DirectAccessGrantsLoginModule.class);
|
||||
|
||||
public static final String SCOPE_OPTION = "scope";
|
||||
|
||||
private String refreshToken;
|
||||
private String scope;
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
|
||||
super.initialize(subject, callbackHandler, sharedState, options);
|
||||
this.scope = (String)options.get(SCOPE_OPTION);
|
||||
|
||||
// This is used just for logout
|
||||
Iterator<RefreshTokenHolder> iterator = subject.getPrivateCredentials(RefreshTokenHolder.class).iterator();
|
||||
@@ -89,6 +94,10 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
|
||||
formparams.add(new BasicNameValuePair("username", username));
|
||||
formparams.add(new BasicNameValuePair("password", password));
|
||||
|
||||
if (scope != null) {
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
|
||||
}
|
||||
|
||||
ClientCredentialsProviderUtils.setClientCredentials(deployment, post, formparams);
|
||||
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
@@ -121,7 +130,8 @@ public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {
|
||||
// refreshToken will be saved to privateCreds of Subject for now
|
||||
refreshToken = tokenResponse.getRefreshToken();
|
||||
|
||||
return bearerAuth(tokenResponse.getToken());
|
||||
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenResponse.getToken(), tokenResponse.getIdToken(), deployment);
|
||||
return postTokenVerification(tokenResponse.getToken(), tokens.getAccessToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.rotation;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AdapterRSATokenVerifier {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class);
|
||||
|
||||
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
|
||||
return verifyToken(tokenString, deployment, true, true);
|
||||
}
|
||||
|
||||
|
||||
public static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
|
||||
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
|
||||
|
||||
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
|
||||
if (publicKey == null) {
|
||||
log.errorf("Didn't find publicKey for kid: %s", kid);
|
||||
throw new VerificationException("Didn't find publicKey for specified kid");
|
||||
}
|
||||
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
|
||||
RSATokenVerifier verifier = RSATokenVerifier.create(tokenString).realmUrl(deployment.getRealmInfoUrl()).checkActive(checkActive).checkTokenType(checkTokenType);
|
||||
PublicKey publicKey = getPublicKey(verifier.getHeader().getKeyId(), deployment);
|
||||
return verifier.publicKey(publicKey).verify().getToken();
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.rotation;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AdapterTokenVerifier {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AdapterTokenVerifier.class);
|
||||
|
||||
|
||||
/**
|
||||
* Verifies bearer token. Typically called when bearer token (access token) is sent to the service, which wants to verify it. Hence it also checks the audience in the token.
|
||||
*
|
||||
* @param tokenString
|
||||
* @param deployment
|
||||
* @return
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
|
||||
TokenVerifier<AccessToken> tokenVerifier = createVerifier(tokenString, deployment, true, AccessToken.class);
|
||||
|
||||
// Verify audience of bearer-token
|
||||
if (deployment.isVerifyTokenAudience()) {
|
||||
tokenVerifier.audience(deployment.getResourceName());
|
||||
}
|
||||
|
||||
return tokenVerifier.verify().getToken();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify access token and ID token. Typically called after successful tokenResponse is received from Keycloak
|
||||
*
|
||||
* @param accessTokenString
|
||||
* @param idTokenString
|
||||
* @param deployment
|
||||
* @return verified and parsed accessToken and idToken
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static VerifiedTokens verifyTokens(String accessTokenString, String idTokenString, KeycloakDeployment deployment) throws VerificationException {
|
||||
// Adapters currently do most of the checks including signature etc on the access token
|
||||
TokenVerifier<AccessToken> tokenVerifier = createVerifier(accessTokenString, deployment, true, AccessToken.class);
|
||||
AccessToken accessToken = tokenVerifier.verify().getToken();
|
||||
|
||||
if (idTokenString != null) {
|
||||
// Don't verify signature again on IDToken
|
||||
IDToken idToken = TokenVerifier.create(idTokenString, IDToken.class).getToken();
|
||||
TokenVerifier<IDToken> idTokenVerifier = TokenVerifier.createWithoutSignature(idToken);
|
||||
|
||||
// Always verify audience and azp on IDToken
|
||||
idTokenVerifier.audience(deployment.getResourceName());
|
||||
idTokenVerifier.issuedFor(deployment.getResourceName());
|
||||
|
||||
idTokenVerifier.verify();
|
||||
return new VerifiedTokens(accessToken, idToken);
|
||||
} else {
|
||||
return new VerifiedTokens(accessToken, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates verifier, initializes it from the KeycloakDeployment and adds the publicKey and some default basic checks (activeness and tokenType). Useful if caller wants to add/remove/update
|
||||
* some checks
|
||||
*
|
||||
* @param tokenString
|
||||
* @param deployment
|
||||
* @param withDefaultChecks
|
||||
* @param tokenClass
|
||||
* @param <T>
|
||||
* @return tokenVerifier
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static <T extends JsonWebToken> TokenVerifier<T> createVerifier(String tokenString, KeycloakDeployment deployment, boolean withDefaultChecks, Class<T> tokenClass) throws VerificationException {
|
||||
TokenVerifier<T> tokenVerifier = TokenVerifier.create(tokenString, tokenClass);
|
||||
|
||||
if (withDefaultChecks) {
|
||||
tokenVerifier
|
||||
.withDefaultChecks()
|
||||
.realmUrl(deployment.getRealmInfoUrl());
|
||||
}
|
||||
|
||||
String kid = tokenVerifier.getHeader().getKeyId();
|
||||
PublicKey publicKey = getPublicKey(kid, deployment);
|
||||
tokenVerifier.publicKey(publicKey);
|
||||
|
||||
return tokenVerifier;
|
||||
}
|
||||
|
||||
|
||||
private static PublicKey getPublicKey(String kid, KeycloakDeployment deployment) throws VerificationException {
|
||||
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
|
||||
|
||||
PublicKey publicKey = pkLocator.getPublicKey(kid, deployment);
|
||||
if (publicKey == null) {
|
||||
log.errorf("Didn't find publicKey for kid: %s", kid);
|
||||
throw new VerificationException("Didn't find publicKey for specified kid");
|
||||
}
|
||||
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
|
||||
public static class VerifiedTokens {
|
||||
|
||||
private final AccessToken accessToken;
|
||||
private final IDToken idToken;
|
||||
|
||||
public VerifiedTokens(AccessToken accessToken, IDToken idToken) {
|
||||
this.accessToken = accessToken;
|
||||
this.idToken = idToken;
|
||||
}
|
||||
|
||||
|
||||
public AccessToken getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public IDToken getIdToken() {
|
||||
return idToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -77,6 +77,7 @@ public class KeycloakDeploymentBuilderTest {
|
||||
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
|
||||
assertEquals(120, deployment.getPublicKeyCacheTtl());
|
||||
assertEquals("/api/$1", deployment.getRedirectRewriteRules().get("^/wsmaster/api/(.*)$"));
|
||||
assertTrue(deployment.isVerifyTokenAudience());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"min-time-between-jwks-requests": 20,
|
||||
"public-key-cache-ttl": 120,
|
||||
"ignore-oauth-query-parameter": true,
|
||||
"verify-token-audience": true,
|
||||
"redirect-rewrite-rules" : {
|
||||
"^/wsmaster/api/(.*)$" : "/api/$1"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -47,7 +47,6 @@
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -86,17 +85,4 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -118,17 +118,4 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
+7
@@ -98,6 +98,12 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.setAllowExpression(true)
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition PUBLIC_KEY_CACHE_TTL =
|
||||
new SimpleAttributeDefinitionBuilder("public-key-cache-ttl", ModelType.INT, true)
|
||||
.setXmlName("public-key-cache-ttl")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.build();
|
||||
|
||||
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
static {
|
||||
@@ -110,6 +116,7 @@ class SecureDeploymentDefinition extends SimpleResourceDefinition {
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_KEY_CACHE_TTL);
|
||||
}
|
||||
|
||||
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
|
||||
+16
@@ -166,6 +166,20 @@ class SharedAttributeDefinitons {
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
|
||||
protected static final SimpleAttributeDefinition PROXY_URL =
|
||||
new SimpleAttributeDefinitionBuilder("proxy-url", ModelType.STRING, true)
|
||||
.setXmlName("proxy-url")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
|
||||
protected static final SimpleAttributeDefinition VERIFY_TOKEN_AUDIENCE =
|
||||
new SimpleAttributeDefinitionBuilder("verify-token-audience", ModelType.BOOLEAN, true)
|
||||
.setXmlName("verify-token-audience")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
@@ -192,6 +206,8 @@ class SharedAttributeDefinitons {
|
||||
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
|
||||
ATTRIBUTES.add(TOKEN_STORE);
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
ATTRIBUTES.add(PROXY_URL);
|
||||
ATTRIBUTES.add(VERIFY_TOKEN_AUDIENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -46,6 +46,8 @@ keycloak.realm.register-node-at-startup=Cluster setting
|
||||
keycloak.realm.register-node-period=how often to re-register node
|
||||
keycloak.realm.token-store=cookie or session storage for auth session data
|
||||
keycloak.realm.principal-attribute=token attribute to use to set Principal name
|
||||
keycloak.realm.proxy-url=The URL for the HTTP proxy if one is used.
|
||||
keycloak.realm.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
|
||||
|
||||
keycloak.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
@@ -82,7 +84,9 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
|
||||
keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
|
||||
keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
|
||||
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
|
||||
|
||||
keycloak.secure-deployment.public-key-cache-ttl=Maximum time the downloaded public keys are considered valid. When this time reach, the adapter is forced to download public keys from keycloak server
|
||||
keycloak.secure-deployment.proxy-url=The URL for the HTTP proxy if one is used.
|
||||
keycloak.secure-deployment.verify-token-audience=If true, then during bearer-only authentication, the adapter will verify if token contains this client name (resource) as an audience
|
||||
keycloak.secure-deployment.credential=Credential value
|
||||
|
||||
keycloak.credential=Credential
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
@@ -107,6 +109,9 @@
|
||||
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="public-key-cache-ttl" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="proxy-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="verify-token-audience" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
|
||||
@@ -30,6 +30,11 @@
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -47,4 +52,4 @@
|
||||
<module>as7-adapter</module>
|
||||
<module>as7-subsystem</module>
|
||||
</modules>
|
||||
</project>
|
||||
</project>
|
||||
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-camel-undertow</artifactId>
|
||||
<name>Keycloak Fuse 7.0 Adapter - Camel + Undertow</name>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.camel.undertow;version="${project.version}"
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
org.keycloak.*;version="${project.version}",
|
||||
org.apache.camel.*,
|
||||
org.apache.camel.component.undertow,
|
||||
io.undertow.*;version="[1.4,3)",
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.enterprise</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-runtime</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-servlet</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-undertow</artifactId>
|
||||
<version>2.21.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.camel</groupId>
|
||||
<artifactId>camel-core</artifactId>
|
||||
<version>2.21.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-Name>${project.name}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||
<Export-Service>org.apache.camel.spi.ComponentResolver;component=undertow-keycloak</Export-Service>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.camel.undertow;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.apache.camel.CamelContext;
|
||||
import org.apache.camel.component.undertow.UndertowComponent;
|
||||
import org.apache.camel.component.undertow.UndertowEndpoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class UndertowKeycloakComponent extends UndertowComponent {
|
||||
|
||||
public UndertowKeycloakComponent() {
|
||||
}
|
||||
|
||||
public UndertowKeycloakComponent(CamelContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UndertowEndpoint createEndpointInstance(URI endpointUri, UndertowComponent component) throws URISyntaxException {
|
||||
return new UndertowKeycloakEndpoint(endpointUri.toString(), component);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getComponentName() {
|
||||
return "undertow-keycloak";
|
||||
}
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.camel.undertow;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.AuthenticatedActionsHandler;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
||||
import org.keycloak.adapters.RequestAuthenticator;
|
||||
import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
|
||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||
import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
|
||||
import org.keycloak.adapters.undertow.OIDCUndertowHttpFacade;
|
||||
import org.keycloak.adapters.undertow.SessionManagementBridge;
|
||||
import org.keycloak.adapters.undertow.UndertowCookieTokenStore;
|
||||
import org.keycloak.adapters.undertow.UndertowRequestAuthenticator;
|
||||
import org.keycloak.adapters.undertow.UndertowSessionTokenStore;
|
||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||
import org.keycloak.enums.TokenStore;
|
||||
import io.undertow.security.api.SecurityContext;
|
||||
import io.undertow.security.idm.Account;
|
||||
import io.undertow.security.idm.Credential;
|
||||
import io.undertow.security.idm.IdentityManager;
|
||||
import io.undertow.security.impl.SecurityContextImpl;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.session.InMemorySessionManager;
|
||||
import io.undertow.server.session.SessionManager;
|
||||
import io.undertow.util.AttachmentKey;
|
||||
import io.undertow.util.StatusCodes;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.camel.Processor;
|
||||
import org.apache.camel.component.undertow.UndertowConsumer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class UndertowKeycloakConsumer extends UndertowConsumer {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(UndertowKeycloakConsumer.class.getName());
|
||||
|
||||
public static final AttachmentKey<KeycloakPrincipal> KEYCLOAK_PRINCIPAL_KEY = AttachmentKey.create(KeycloakPrincipal.class);
|
||||
|
||||
private static final IdentityManager IDENTITY_MANAGER = new IdentityManager() {
|
||||
@Override
|
||||
public Account verify(Account account) {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(String id, Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
};
|
||||
|
||||
protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
||||
|
||||
protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
|
||||
private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
|
||||
|
||||
protected final AdapterDeploymentContext deploymentContext;
|
||||
|
||||
protected final SessionManager sessionManager;
|
||||
|
||||
protected final List<String> allowedRoles;
|
||||
|
||||
private final int confidentialPort;
|
||||
|
||||
private final Pattern skipPattern;
|
||||
|
||||
public UndertowKeycloakConsumer(UndertowKeycloakEndpoint endpoint, Processor processor,
|
||||
AdapterDeploymentContext deploymentContext, Pattern skipPattern, List<String> allowedRoles, int confidentialPort) {
|
||||
super(endpoint, processor);
|
||||
this.sessionManager = new InMemorySessionManager(endpoint.getEndpointUri());
|
||||
this.deploymentContext = deploymentContext;
|
||||
this.skipPattern = skipPattern;
|
||||
this.confidentialPort = confidentialPort;
|
||||
this.allowedRoles = allowedRoles == null ? Collections.<String>emptyList() : allowedRoles;
|
||||
}
|
||||
|
||||
public int getConfidentialPort() {
|
||||
return confidentialPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange httpExchange) throws Exception {
|
||||
if (shouldSkip(httpExchange.getRequestPath())) {
|
||||
super.handleRequest(httpExchange);
|
||||
return;
|
||||
}
|
||||
|
||||
//perform only non-blocking operation on exchange
|
||||
if (httpExchange.isInIoThread()) {
|
||||
httpExchange.dispatch(this);
|
||||
return;
|
||||
}
|
||||
|
||||
OIDCUndertowHttpFacade facade = new OIDCUndertowHttpFacade(httpExchange);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
|
||||
if (deployment == null || !deployment.isConfigured()) {
|
||||
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
|
||||
LOG.fine("deployment not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.fine("executing PreAuthActionsHandler");
|
||||
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
|
||||
PreAuthActionsHandler preAuth = new PreAuthActionsHandler(bridge, deploymentContext, facade);
|
||||
if (preAuth.handleRequest()) return;
|
||||
|
||||
SecurityContext securityContext = httpExchange.getSecurityContext();
|
||||
if (securityContext == null) {
|
||||
securityContext = new SecurityContextImpl(httpExchange, IDENTITY_MANAGER);
|
||||
}
|
||||
AdapterTokenStore tokenStore = getTokenStore(httpExchange, facade, deployment, securityContext);
|
||||
tokenStore.checkCurrentToken();
|
||||
|
||||
LOG.fine("executing AuthenticatedActionsHandler");
|
||||
RequestAuthenticator authenticator = new UndertowRequestAuthenticator(facade, deployment, confidentialPort, securityContext, httpExchange, tokenStore);
|
||||
AuthOutcome outcome = authenticator.authenticate();
|
||||
|
||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||
LOG.fine("AUTHENTICATED");
|
||||
if (httpExchange.isResponseComplete()) {
|
||||
return;
|
||||
}
|
||||
AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
|
||||
if (actions.handledRequest()) {
|
||||
return;
|
||||
} else {
|
||||
final Account authenticatedAccount = securityContext.getAuthenticatedAccount();
|
||||
if (authenticatedAccount instanceof KeycloakUndertowAccount) {
|
||||
final KeycloakUndertowAccount kua = (KeycloakUndertowAccount) authenticatedAccount;
|
||||
httpExchange.putAttachment(KEYCLOAK_PRINCIPAL_KEY, (KeycloakPrincipal) kua.getPrincipal());
|
||||
}
|
||||
|
||||
Set<String> roles = Optional
|
||||
.ofNullable(authenticatedAccount.getRoles())
|
||||
.orElse((Set<String>) Collections.EMPTY_SET);
|
||||
|
||||
LOG.log(Level.FINE, "Allowed roles: {0}, current roles: {1}", new Object[] {allowedRoles, roles});
|
||||
|
||||
if (isRoleAllowed(roles, httpExchange)) {
|
||||
super.handleRequest(httpExchange);
|
||||
} else {
|
||||
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
LOG.fine("challenge");
|
||||
challenge.challenge(facade);
|
||||
return;
|
||||
}
|
||||
|
||||
httpExchange.setStatusCode(StatusCodes.FORBIDDEN);
|
||||
}
|
||||
|
||||
public boolean isRoleAllowed(Set<String> roles, HttpServerExchange httpExchange) throws Exception {
|
||||
for (String role : allowedRoles) {
|
||||
if (roles.contains(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected AdapterTokenStore getTokenStore(HttpServerExchange exchange, HttpFacade facade, KeycloakDeployment deployment, SecurityContext securityContext) {
|
||||
if (deployment.getTokenStore() == TokenStore.SESSION) {
|
||||
return new UndertowSessionTokenStore(exchange, deployment, userSessionManagement, securityContext);
|
||||
} else {
|
||||
return new UndertowCookieTokenStore(facade, deployment, securityContext);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSkip(String requestPath) {
|
||||
return skipPattern != null && skipPattern.matcher(requestPath).matches();
|
||||
}
|
||||
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.camel.undertow;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.camel.Consumer;
|
||||
import org.apache.camel.Exchange;
|
||||
import org.apache.camel.Processor;
|
||||
import org.apache.camel.component.undertow.UndertowComponent;
|
||||
import org.apache.camel.component.undertow.UndertowEndpoint;
|
||||
import static org.keycloak.adapters.camel.undertow.UndertowKeycloakConsumer.KEYCLOAK_PRINCIPAL_KEY;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class UndertowKeycloakEndpoint extends UndertowEndpoint {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(UndertowKeycloakEndpoint.class.getName());
|
||||
|
||||
private KeycloakConfigResolver configResolver;
|
||||
|
||||
private AdapterConfig adapterConfig;
|
||||
|
||||
private String skipPattern;
|
||||
|
||||
private List<String> allowedRoles = Collections.emptyList();
|
||||
|
||||
private int confidentialPort = 8443;
|
||||
|
||||
public UndertowKeycloakEndpoint(String uri, UndertowComponent component) {
|
||||
super(uri, component);
|
||||
}
|
||||
|
||||
public AdapterConfig getAdapterConfig() {
|
||||
return adapterConfig;
|
||||
}
|
||||
|
||||
public void setAdapterConfig(AdapterConfig adapterConfig) {
|
||||
LOG.info("adapterConfig");
|
||||
this.adapterConfig = adapterConfig;
|
||||
}
|
||||
|
||||
public String getSkipPattern() {
|
||||
return skipPattern;
|
||||
}
|
||||
|
||||
public void setSkipPattern(String skipPattern) {
|
||||
this.skipPattern = skipPattern;
|
||||
}
|
||||
|
||||
public List<String> getAllowedRoles() {
|
||||
return allowedRoles;
|
||||
}
|
||||
|
||||
public void setAllowedRoles(List<String> allowedRoles) {
|
||||
this.allowedRoles = allowedRoles;
|
||||
}
|
||||
|
||||
public void setAllowedRoles(String allowedRoles) {
|
||||
this.allowedRoles = allowedRoles == null ? null : Arrays.asList(allowedRoles.split("\\s*,\\s*"));
|
||||
}
|
||||
|
||||
public int getConfidentialPort() {
|
||||
return confidentialPort;
|
||||
}
|
||||
|
||||
public void setConfidentialPort(int confidentialPort) {
|
||||
this.confidentialPort = confidentialPort;
|
||||
}
|
||||
|
||||
public KeycloakConfigResolver getConfigResolver() {
|
||||
return configResolver;
|
||||
}
|
||||
|
||||
public void setConfigResolver(KeycloakConfigResolver configResolver) {
|
||||
this.configResolver = configResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer createConsumer(Processor processor) throws Exception {
|
||||
return new UndertowKeycloakConsumer(this, processor, getDeploymentContext(), getSkipPatternAsPattern(), computeAllowedRoles(), this.confidentialPort);
|
||||
}
|
||||
|
||||
public List<String> computeAllowedRoles() {
|
||||
List<String> res = this.allowedRoles == null ? Collections.<String>emptyList() : this.allowedRoles;
|
||||
if (res.isEmpty()) {
|
||||
LOG.warning("No roles were configured, Keycloak will deny every request");
|
||||
}
|
||||
LOG.log(Level.FINE, "Allowed roles: {0}", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exchange createExchange(HttpServerExchange httpExchange) throws Exception {
|
||||
final Exchange res = super.createExchange(httpExchange);
|
||||
|
||||
KeycloakPrincipal principal = httpExchange.getAttachment(KEYCLOAK_PRINCIPAL_KEY);
|
||||
LOG.log(Level.FINE, "principal: {0}", principal);
|
||||
if (principal != null) {
|
||||
res.setProperty(KeycloakPrincipal.class.getName(), principal);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private AdapterDeploymentContext getDeploymentContext() {
|
||||
if (configResolver != null) {
|
||||
LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
|
||||
return new AdapterDeploymentContext(configResolver);
|
||||
} else if (adapterConfig != null) {
|
||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
|
||||
return new AdapterDeploymentContext(kd);
|
||||
}
|
||||
|
||||
LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
|
||||
return new AdapterDeploymentContext();
|
||||
}
|
||||
|
||||
private Pattern getSkipPatternAsPattern() {
|
||||
return skipPattern == null
|
||||
? null
|
||||
: Pattern.compile(skipPattern, Pattern.DOTALL);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
class=org.keycloak.adapters.camel.undertow.UndertowKeycloakComponent
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-pax-web-jetty94</artifactId>
|
||||
<name>Keycloak Fuse 7.0 Adapter - Jetty 9.4</name>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<properties>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.osgi.jetty94.*;version="${project.version}"
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
!org.keycloak.adapters.osgi.jetty94,
|
||||
org.ops4j.pax.web.*;version="[7.1,9)",
|
||||
org.keycloak.*;version="${project.version}",
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-jetty</keycloak.osgi.fragment>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.enterprise</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty94-adapter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-security</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-Name>${project.name}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.osgi.jetty94;
|
||||
|
||||
import org.ops4j.pax.web.service.AuthenticatorService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class KeycloakAuthenticatorService implements AuthenticatorService {
|
||||
|
||||
@Override
|
||||
public <T> T getAuthenticatorService(String method, Class<T> iface) {
|
||||
if (method == null || iface != org.eclipse.jetty.security.Authenticator.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("KEYCLOAK".equalsIgnoreCase(method)) {
|
||||
return iface.cast(new org.keycloak.adapters.jetty.KeycloakJettyAuthenticator());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.osgi.jetty94;
|
||||
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.ops4j.pax.web.service.WebContainer;
|
||||
import org.ops4j.pax.web.service.spi.model.SecurityConstraintMappingModel;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.service.http.HttpContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import org.osgi.util.tracker.ServiceTrackerCustomizer;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Integration with pax-web, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
|
||||
* and allows to inject custom security constraint for securing resources by Keycloak.
|
||||
*
|
||||
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
|
||||
* is true in karaf/fuse environment</p>
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PaxWebIntegrationService {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(PaxWebIntegrationService.class);
|
||||
|
||||
private BundleContext bundleContext;
|
||||
private String jettyWebXmlLocation;
|
||||
private List<Object> constraintMappings;
|
||||
|
||||
private ServiceTracker webContainerTracker;
|
||||
private HttpContext httpContext;
|
||||
|
||||
public BundleContext getBundleContext() {
|
||||
return bundleContext;
|
||||
}
|
||||
|
||||
public void setBundleContext(BundleContext bundleContext) {
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
|
||||
public String getJettyWebXmlLocation() {
|
||||
return jettyWebXmlLocation;
|
||||
}
|
||||
|
||||
public void setJettyWebXmlLocation(String jettyWebXmlLocation) {
|
||||
this.jettyWebXmlLocation = jettyWebXmlLocation;
|
||||
}
|
||||
|
||||
public List<Object> getConstraintMappings() {
|
||||
return constraintMappings;
|
||||
}
|
||||
|
||||
public void setConstraintMappings(List<Object> constraintMappings) {
|
||||
this.constraintMappings = constraintMappings;
|
||||
}
|
||||
|
||||
protected ServiceTracker getWebContainerTracker() {
|
||||
return webContainerTracker;
|
||||
}
|
||||
|
||||
protected HttpContext getHttpContext() {
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
public void setHttpContext(HttpContext httpContext) {
|
||||
this.httpContext = httpContext;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
|
||||
|
||||
@Override
|
||||
public Object addingService(ServiceReference reference) {
|
||||
return addingWebContainerCallback(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifiedService(ServiceReference reference, Object service) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference reference, Object service) {
|
||||
removingWebContainerCallback(reference);
|
||||
}
|
||||
};
|
||||
|
||||
webContainerTracker = new ServiceTracker(bundleContext, WebContainer.class.getName(), trackerCustomizer);
|
||||
webContainerTracker.open();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
webContainerTracker.remove(webContainerTracker.getServiceReference());
|
||||
}
|
||||
|
||||
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
|
||||
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
|
||||
if (httpContext == null) {
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
}
|
||||
|
||||
addJettyWebXml(service);
|
||||
|
||||
if (constraintMappings == null) {
|
||||
throw new IllegalStateException("constraintMappings was null!");
|
||||
}
|
||||
List<ConstraintHandler> handlers = new ArrayList<>();
|
||||
try {
|
||||
handlers.add(new JettyConstraintHandler());
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
try {
|
||||
handlers.add(new PaxWebConstraintHandler());
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
for (Object constraintMapping : constraintMappings) {
|
||||
boolean handled = false;
|
||||
for (ConstraintHandler handler : handlers) {
|
||||
handled |= handler.addConstraintMapping(httpContext, service, constraintMapping);
|
||||
}
|
||||
if (!handled) {
|
||||
log.warnv("Unable to add constraint mapping for constraint of type " + constraintMapping.getClass().toString());
|
||||
}
|
||||
}
|
||||
|
||||
service.registerLoginConfig("BASIC", "does-not-matter", null, null, httpContext);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
protected void addJettyWebXml(WebContainer service) {
|
||||
String jettyWebXmlLoc;
|
||||
if (this.jettyWebXmlLocation == null) {
|
||||
jettyWebXmlLoc = "/WEB-INF/jetty-web.xml";
|
||||
} else {
|
||||
jettyWebXmlLoc = this.jettyWebXmlLocation;
|
||||
}
|
||||
|
||||
URL jettyWebXml = bundleContext.getBundle().getResource(jettyWebXmlLoc);
|
||||
if (jettyWebXml != null) {
|
||||
log.debug("Found jetty-web XML configuration on bundle classpath on " + jettyWebXmlLoc);
|
||||
service.registerJettyWebXml(jettyWebXml, httpContext);
|
||||
} else {
|
||||
log.debug("Not found jetty-web XML configuration on bundle classpath on " + jettyWebXmlLoc);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addConstraintMapping(WebContainer service, SecurityConstraintMappingModel constraintMapping) {
|
||||
String name = constraintMapping.getConstraintName();
|
||||
if (name == null) {
|
||||
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
|
||||
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
|
||||
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
|
||||
}
|
||||
|
||||
protected void addConstraintMapping(WebContainer service, ConstraintMapping constraintMapping) {
|
||||
Constraint constraint = constraintMapping.getConstraint();
|
||||
String[] roles = constraint.getRoles();
|
||||
// name property is unavailable on constraint object :/
|
||||
|
||||
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
|
||||
int dataConstraint = constraint.getDataConstraint();
|
||||
String dataConstraintStr;
|
||||
switch (dataConstraint) {
|
||||
case Constraint.DC_UNSET: dataConstraintStr = null; break;
|
||||
case Constraint.DC_NONE: dataConstraintStr = "NONE"; break;
|
||||
case Constraint.DC_CONFIDENTIAL: dataConstraintStr = "CONFIDENTIAL"; break;
|
||||
case Constraint.DC_INTEGRAL: dataConstraintStr = "INTEGRAL"; break;
|
||||
default:
|
||||
log.warnv("Unknown data constraint: " + dataConstraint);
|
||||
dataConstraintStr = "CONFIDENTIAL";
|
||||
}
|
||||
List<String> rolesList = Arrays.asList(roles);
|
||||
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
|
||||
+ constraint.getAuthenticate() + ", roles=" + rolesList);
|
||||
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
|
||||
}
|
||||
|
||||
protected void removingWebContainerCallback(ServiceReference serviceReference) {
|
||||
WebContainer service = (WebContainer)bundleContext.getService(serviceReference);
|
||||
if (service != null) {
|
||||
service.unregisterLoginConfig(httpContext);
|
||||
service.unregisterConstraintMapping(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private interface ConstraintHandler {
|
||||
boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm);
|
||||
}
|
||||
|
||||
private static class PaxWebConstraintHandler implements ConstraintHandler {
|
||||
|
||||
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
|
||||
if (cm instanceof SecurityConstraintMappingModel) {
|
||||
SecurityConstraintMappingModel constraintMapping = (SecurityConstraintMappingModel) cm;
|
||||
String name = constraintMapping.getConstraintName();
|
||||
if (name == null) {
|
||||
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
|
||||
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
|
||||
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class JettyConstraintHandler implements ConstraintHandler {
|
||||
|
||||
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
|
||||
if (cm instanceof ConstraintMapping) {
|
||||
ConstraintMapping constraintMapping = (ConstraintMapping) cm;
|
||||
Constraint constraint = constraintMapping.getConstraint();
|
||||
String[] roles = constraint.getRoles();
|
||||
// name property is unavailable on constraint object :/
|
||||
|
||||
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
|
||||
int dataConstraint = constraint.getDataConstraint();
|
||||
String dataConstraintStr;
|
||||
switch (dataConstraint) {
|
||||
case Constraint.DC_UNSET:
|
||||
dataConstraintStr = null;
|
||||
break;
|
||||
case Constraint.DC_NONE:
|
||||
dataConstraintStr = "NONE";
|
||||
break;
|
||||
case Constraint.DC_CONFIDENTIAL:
|
||||
dataConstraintStr = "CONFIDENTIAL";
|
||||
break;
|
||||
case Constraint.DC_INTEGRAL:
|
||||
dataConstraintStr = "INTEGRAL";
|
||||
break;
|
||||
default:
|
||||
log.warnv("Unknown data constraint: " + dataConstraint);
|
||||
dataConstraintStr = "CONFIDENTIAL";
|
||||
}
|
||||
List<String> rolesList = Arrays.asList(roles);
|
||||
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
|
||||
+ constraint.getAuthenticate() + ", roles=" + rolesList);
|
||||
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.adapters.osgi.jetty94.KeycloakAuthenticatorService
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Keycloak Fuse 7.0 Integration</name>
|
||||
|
||||
<properties>
|
||||
<fuse7.version>7.0.1-SNAPSHOT</fuse7.version>
|
||||
<tomcat.version>8.0.14</tomcat.version>
|
||||
<jetty9.version>9.4.2.v20170220</jetty9.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>camel-undertow</module>
|
||||
<module>undertow</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>jetty94</module>
|
||||
<module>tomcat8</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
Executable → Regular
+41
-27
@@ -19,47 +19,66 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Keycloak OSGI Thirdparty</name>
|
||||
<description>Keycloak OSGI bundling for 3rd party libs without OSGI headers in manifest</description>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-osgi-thirdparty</artifactId>
|
||||
<artifactId>keycloak-pax-web-tomcat8</artifactId>
|
||||
<name>Keycloak Fuse 7.0 Adapter - Tomcat 8</name>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<properties>
|
||||
<keycloak.osgi.export>
|
||||
org.apache.http.*;version=${apache.httpcomponents.version}
|
||||
org.keycloak.adapters.osgi.tomcat.*;version="${project.version}"
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
!org.keycloak.adapters.osgi.tomcat,
|
||||
org.ops4j.pax.web.*;version="[7.1,9)",
|
||||
org.keycloak.*;version="${project.version}",
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-tomcat</keycloak.osgi.fragment>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-catalina</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
@@ -75,19 +94,14 @@
|
||||
</executions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Embed-Dependency>*;scope=compile|runtime;artifactId=!httpclient|httpcore</Embed-Dependency>
|
||||
<Embed-Transitive>true</Embed-Transitive>
|
||||
<Bundle-ClassPath>.</Bundle-ClassPath>
|
||||
<Bundle-Name>${project.name}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||
<Implementation-Title>${project.name}</Implementation-Title>
|
||||
<Implementation-Version>${project.version}</Implementation-Version>
|
||||
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
+19
-14
@@ -14,23 +14,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
|
||||
import {KeycloakService} from './keycloak.service';
|
||||
|
||||
@Injectable()
|
||||
export class KeycloakGuard implements CanActivateChild {
|
||||
constructor(private keycloakService: KeycloakService) {}
|
||||
|
||||
canActivateChild(route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): boolean {
|
||||
package org.keycloak.adapters.osgi.tomcat;
|
||||
|
||||
if (this.keycloakService.authenticated()) {
|
||||
return true;
|
||||
import org.ops4j.pax.web.service.AuthenticatorService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class KeycloakAuthenticatorService implements AuthenticatorService {
|
||||
|
||||
@Override
|
||||
public <T> T getAuthenticatorService(String method, Class<T> iface) {
|
||||
if (method == null || iface != org.apache.catalina.Valve.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.keycloakService.login();
|
||||
return false;
|
||||
if ("KEYCLOAK".equalsIgnoreCase(method)) {
|
||||
return iface.cast(new org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.adapters.osgi.tomcat.KeycloakAuthenticatorService
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-fuse7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-pax-web-undertow</artifactId>
|
||||
<name>Keycloak Fuse 7.0 Adapter - Undertow</name>
|
||||
<packaging>bundle</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.osgi.undertow.*;version="${project.version}"
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
!org.keycloak.adapters.osgi.undertow,
|
||||
org.keycloak.*;version="${project.version}",
|
||||
org.keycloak.adapters.osgi.*;version="${project.version}",
|
||||
org.ops4j.pax.web.*;version="[7.1,9)",
|
||||
javax.servlet.*;version="[2.5,4)";resolution:=optional,
|
||||
org.apache.cxf.transport.http;resolution:=optional;version="[3,4)",
|
||||
org.apache.cxf.transport.http_undertow;resolution:=optional;version="[3,4)",
|
||||
org.apache.cxf.transport.servlet;resolution:=optional;version="[3,4)",
|
||||
io.undertow.*;version="[1.4,3)",
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
<keycloak.osgi.fragment>org.ops4j.pax.web.pax-web-undertow</keycloak.osgi.fragment>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.enterprise</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-transports-http</artifactId>
|
||||
<version>${cxf.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cxf</groupId>
|
||||
<artifactId>cxf-rt-transports-http-undertow</artifactId>
|
||||
<version>${cxf.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-servlet</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-osgi-adapter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-Name>${project.name}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||
<Fragment-Host>${keycloak.osgi.fragment}</Fragment-Host>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.osgi.undertow;
|
||||
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.spi.InMemorySessionIdMapper;
|
||||
import org.keycloak.adapters.spi.SessionIdMapper;
|
||||
import org.keycloak.adapters.undertow.UndertowAuthenticationMechanism;
|
||||
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import io.undertow.security.api.AuthenticationMechanism;
|
||||
import io.undertow.security.api.AuthenticationMode;
|
||||
import io.undertow.security.handlers.AuthenticationCallHandler;
|
||||
import io.undertow.security.handlers.AuthenticationConstraintHandler;
|
||||
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
|
||||
import io.undertow.security.handlers.SecurityInitialHandler;
|
||||
import io.undertow.security.idm.Account;
|
||||
import io.undertow.security.idm.Credential;
|
||||
import io.undertow.security.idm.IdentityManager;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.cxf.transport.http_undertow.CXFUndertowHttpHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class CxfKeycloakAuthHandler implements CXFUndertowHttpHandler {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CxfKeycloakAuthHandler.class.getName());
|
||||
|
||||
private static final IdentityManager IDENTITY_MANAGER = new IdentityManager() {
|
||||
@Override
|
||||
public Account verify(Account account) {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(String id, Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
};
|
||||
|
||||
private final UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement();
|
||||
|
||||
protected final NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
|
||||
protected final SessionIdMapper idMapper = new InMemorySessionIdMapper();
|
||||
|
||||
private final AtomicReference<HttpHandler> securityHandler = new AtomicReference<>();
|
||||
|
||||
private Pattern skipPattern;
|
||||
|
||||
private int confidentialPort = 8443;
|
||||
|
||||
private HttpHandler next;
|
||||
|
||||
private KeycloakConfigResolver configResolver;
|
||||
|
||||
private AdapterConfig adapterConfig;
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
if (shouldSkip(exchange.getRequestPath())) {
|
||||
next.handleRequest(exchange);
|
||||
} else {
|
||||
getSecurityHandler().handleRequest(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHandler getSecurityHandler() {
|
||||
if (this.securityHandler.get() == null) {
|
||||
HttpHandler handler = this.next;
|
||||
|
||||
handler = new AuthenticationCallHandler(handler);
|
||||
handler = new AuthenticationConstraintHandler(handler);
|
||||
|
||||
AdapterDeploymentContext deploymentContext = buildDeploymentContext();
|
||||
|
||||
final List<AuthenticationMechanism> mechanisms
|
||||
= Collections.<AuthenticationMechanism>singletonList(
|
||||
new UndertowAuthenticationMechanism(deploymentContext, userSessionManagement, nodesRegistrationManagement, confidentialPort, null));
|
||||
handler = new AuthenticationMechanismsHandler(handler, mechanisms);
|
||||
|
||||
this.securityHandler.compareAndSet(null, new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, IDENTITY_MANAGER, "KEYCLOAK", handler));
|
||||
}
|
||||
|
||||
return this.securityHandler.get();
|
||||
}
|
||||
|
||||
private AdapterDeploymentContext buildDeploymentContext() {
|
||||
if (configResolver != null) {
|
||||
LOG.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolver.getClass());
|
||||
return new AdapterDeploymentContext(configResolver);
|
||||
} else if (adapterConfig != null) {
|
||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(adapterConfig);
|
||||
return new AdapterDeploymentContext(kd);
|
||||
}
|
||||
|
||||
LOG.warning("Adapter is unconfigured, Keycloak will deny every request");
|
||||
return new AdapterDeploymentContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNext(HttpHandler nextHandler) {
|
||||
this.next = nextHandler;
|
||||
}
|
||||
|
||||
private boolean shouldSkip(String requestPath) {
|
||||
return skipPattern != null && skipPattern.matcher(requestPath).matches();
|
||||
}
|
||||
|
||||
public KeycloakConfigResolver getConfigResolver() {
|
||||
return configResolver;
|
||||
}
|
||||
|
||||
public void setConfigResolver(KeycloakConfigResolver configResolver) {
|
||||
this.configResolver = configResolver;
|
||||
}
|
||||
|
||||
public int getConfidentialPort() {
|
||||
return confidentialPort;
|
||||
}
|
||||
|
||||
public void setConfidentialPort(int confidentialPort) {
|
||||
this.confidentialPort = confidentialPort;
|
||||
}
|
||||
|
||||
public AdapterConfig getAdapterConfig() {
|
||||
return adapterConfig;
|
||||
}
|
||||
|
||||
public void setAdapterConfig(AdapterConfig adapterConfig) {
|
||||
this.adapterConfig = adapterConfig;
|
||||
}
|
||||
|
||||
public String getSkipPattern() {
|
||||
return skipPattern.pattern();
|
||||
}
|
||||
|
||||
public void setSkipPattern(String skipPattern) {
|
||||
this.skipPattern = Pattern.compile(skipPattern, Pattern.DOTALL);
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.osgi.undertow;
|
||||
|
||||
import org.ops4j.pax.web.service.AuthenticatorService;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class KeycloakAuthenticatorService implements AuthenticatorService {
|
||||
|
||||
@Override
|
||||
public <T> T getAuthenticatorService(String method, Class<T> iface) {
|
||||
if (method == null || iface != io.undertow.servlet.ServletExtension.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("KEYCLOAK".equalsIgnoreCase(method)) {
|
||||
return iface.cast(new org.keycloak.adapters.undertow.KeycloakServletExtension());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.osgi.undertow;
|
||||
|
||||
import org.keycloak.adapters.osgi.PaxWebSecurityConstraintMapping;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.ops4j.pax.web.service.WebContainer;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.service.http.HttpContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
import org.osgi.util.tracker.ServiceTrackerCustomizer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Integration with pax-web, which allows to inject custom security constraint for securing resources by Keycloak.
|
||||
*
|
||||
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
|
||||
* is true in karaf/fuse environment</p>
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PaxWebIntegrationService {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(PaxWebIntegrationService.class);
|
||||
|
||||
private BundleContext bundleContext;
|
||||
private List<PaxWebSecurityConstraintMapping> constraintMappings;
|
||||
|
||||
private ServiceTracker webContainerTracker;
|
||||
private HttpContext httpContext;
|
||||
|
||||
public BundleContext getBundleContext() {
|
||||
return bundleContext;
|
||||
}
|
||||
|
||||
public void setBundleContext(BundleContext bundleContext) {
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
|
||||
public List<PaxWebSecurityConstraintMapping> getConstraintMappings() {
|
||||
return constraintMappings;
|
||||
}
|
||||
|
||||
public void setConstraintMappings(List<PaxWebSecurityConstraintMapping> constraintMappings) {
|
||||
this.constraintMappings = constraintMappings;
|
||||
}
|
||||
|
||||
protected ServiceTracker getWebContainerTracker() {
|
||||
return webContainerTracker;
|
||||
}
|
||||
|
||||
protected HttpContext getHttpContext() {
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
public void setHttpContext(HttpContext httpContext) {
|
||||
this.httpContext = httpContext;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
ServiceTrackerCustomizer trackerCustomizer = new ServiceTrackerCustomizer() {
|
||||
|
||||
@Override
|
||||
public Object addingService(ServiceReference reference) {
|
||||
return addingWebContainerCallback(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifiedService(ServiceReference reference, Object service) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference reference, Object service) {
|
||||
removingWebContainerCallback(reference);
|
||||
}
|
||||
};
|
||||
|
||||
webContainerTracker = new ServiceTracker(bundleContext, WebContainer.class.getName(), trackerCustomizer);
|
||||
webContainerTracker.open();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
webContainerTracker.remove(webContainerTracker.getServiceReference());
|
||||
}
|
||||
|
||||
protected WebContainer addingWebContainerCallback(ServiceReference webContainerServiceReference) {
|
||||
WebContainer service = (WebContainer) bundleContext.getService(webContainerServiceReference);
|
||||
if (httpContext == null) {
|
||||
httpContext = service.createDefaultHttpContext();
|
||||
}
|
||||
|
||||
if (constraintMappings == null) {
|
||||
throw new IllegalStateException("constraintMappings was null!");
|
||||
}
|
||||
for (PaxWebSecurityConstraintMapping constraintMapping : constraintMappings) {
|
||||
addConstraintMapping(service, constraintMapping);
|
||||
}
|
||||
|
||||
service.registerLoginConfig("KEYCLOAK", "specified-in-keycloak-json", null, null, httpContext);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
protected void addConstraintMapping(WebContainer service, PaxWebSecurityConstraintMapping cm) {
|
||||
log.debug("Adding security constraint name=" + cm.getConstraintName() + ", url=" + cm.getUrl() + ", dataConstraint=" + cm.getDataConstraint() + ", canAuthenticate="
|
||||
+ cm.isAuthentication() + ", roles=" + cm.getRoles());
|
||||
service.registerConstraintMapping(
|
||||
cm.getConstraintName(),
|
||||
cm.getMapping(),
|
||||
cm.getUrl(),
|
||||
cm.getDataConstraint(),
|
||||
cm.isAuthentication(),
|
||||
cm.getRoles(),
|
||||
httpContext
|
||||
);
|
||||
}
|
||||
|
||||
protected void removingWebContainerCallback(ServiceReference serviceReference) {
|
||||
WebContainer service = (WebContainer)bundleContext.getService(serviceReference);
|
||||
if (service != null) {
|
||||
service.unregisterLoginConfig(httpContext);
|
||||
service.unregisterConstraintMapping(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.adapters.osgi.undertow.KeycloakAuthenticatorService
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -69,22 +69,9 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
|
||||
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+4
-13
@@ -24,16 +24,13 @@ import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.adapters.rotation.AdapterTokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.Form;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
@@ -537,15 +534,9 @@ public class KeycloakInstalled {
|
||||
refreshToken = tokenResponse.getRefreshToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
if (idTokenString != null) {
|
||||
try {
|
||||
JWSInput input = new JWSInput(idTokenString);
|
||||
idToken = input.readJsonContent(IDToken.class);
|
||||
} catch (JWSInputException e) {
|
||||
throw new VerificationException();
|
||||
}
|
||||
}
|
||||
AdapterTokenVerifier.VerifiedTokens tokens = AdapterTokenVerifier.verifyTokens(tokenString, idTokenString, deployment);
|
||||
token = tokens.getAccessToken();
|
||||
idToken = tokens.getIdToken();
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -33,7 +33,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
|
||||
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -87,17 +87,5 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -14,21 +14,24 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
<name>Keycloak Jetty Core Integration</name>
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
<name>Keycloak Jetty Core Integration</name>
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<jetty9.version>8.1.17.v20150415</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.jetty.core.*
|
||||
@@ -40,18 +43,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-spi</artifactId>
|
||||
@@ -61,29 +63,29 @@
|
||||
<artifactId>keycloak-jetty-adapter-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -105,23 +107,14 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -154,7 +147,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+2
-2
@@ -85,7 +85,7 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
CookieTokenStore.removeCookie(facade);
|
||||
CookieTokenStore.removeCookie(deployment, facade);
|
||||
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class JettyCookieTokenStore implements AdapterTokenStore {
|
||||
if (success && session.isActive()) return principal;
|
||||
|
||||
log.debugf("Cleanup and expire cookie for user %s after failed refresh", principal.getName());
|
||||
CookieTokenStore.removeCookie(facade);
|
||||
CookieTokenStore.removeCookie(deployment, facade);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,21 +14,24 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty81-adapter</artifactId>
|
||||
<name>Keycloak Jetty 8.1.x Integration</name>
|
||||
<artifactId>keycloak-jetty81-adapter</artifactId>
|
||||
<name>Keycloak Jetty 8.1.x Integration</name>
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<jetty9.version>8.1.17.v20150415</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.jetty.*
|
||||
@@ -39,18 +42,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
@@ -63,26 +65,26 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -104,23 +106,14 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -153,7 +146,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -14,20 +14,20 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty91-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.1.x Integration</name>
|
||||
<artifactId>keycloak-jetty91-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.1.x Integration</name>
|
||||
<properties>
|
||||
<jetty9.version>9.1.5.v20140505</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
@@ -40,18 +40,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
@@ -67,33 +66,33 @@
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</exclusion>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-security</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -115,22 +114,22 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
@@ -164,7 +163,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -14,21 +14,24 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty92-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.2.x Integration</name>
|
||||
<artifactId>keycloak-jetty92-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.2.x Integration</name>
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<jetty9.version>9.2.4.v20141103</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.jetty.*
|
||||
@@ -40,18 +43,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
@@ -60,26 +62,26 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -101,23 +103,14 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -150,7 +143,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -14,20 +14,20 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty93-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.3.x Integration</name>
|
||||
<artifactId>keycloak-jetty93-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.3.x Integration</name>
|
||||
<properties>
|
||||
<jetty9.version>9.3.9.v20160517</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
@@ -40,18 +40,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
@@ -60,26 +59,26 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -101,23 +100,14 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -150,7 +140,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -14,20 +14,20 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-jetty94-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.4.x Integration</name>
|
||||
<artifactId>keycloak-jetty94-adapter</artifactId>
|
||||
<name>Keycloak Jetty 9.4.x Integration</name>
|
||||
<properties>
|
||||
<jetty9.version>9.4.2.v20170220</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
@@ -40,18 +40,17 @@
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
@@ -60,26 +59,26 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
@@ -101,23 +100,14 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -150,7 +140,7 @@
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Jetty Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+73
-10
@@ -18,7 +18,7 @@
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import * as Keycloak from 'keycloak';
|
||||
import * as Keycloak from './keycloak';
|
||||
|
||||
export as namespace KeycloakAuthorization;
|
||||
|
||||
@@ -35,6 +35,64 @@ declare namespace KeycloakAuthorization {
|
||||
then(onGrant: (rpt: string) => void, onDeny: () => void, onError: () => void): void;
|
||||
}
|
||||
|
||||
interface AuthorizationRequest {
|
||||
/**
|
||||
* An array of objects representing the resource and scopes.
|
||||
*/
|
||||
permissions?:ResourcePermission[],
|
||||
|
||||
/**
|
||||
* A permission ticket obtained from a resource server when using UMA authorization protocol.
|
||||
*/
|
||||
ticket?:string,
|
||||
|
||||
/**
|
||||
* A boolean value indicating whether the server should create permission requests to the resources
|
||||
* and scopes referenced by a permission ticket. This parameter will only take effect when used together
|
||||
* with the ticket parameter as part of a UMA authorization process.
|
||||
*/
|
||||
submitRequest?:boolean,
|
||||
|
||||
/**
|
||||
* Defines additional information about this authorization request in order to specify how it should be processed
|
||||
* by the server.
|
||||
*/
|
||||
metadata?:AuthorizationRequestMetadata,
|
||||
|
||||
/**
|
||||
* Defines whether or not this authorization request should include the current RPT. If set to true, the RPT will
|
||||
* be sent and permissions in the current RPT will be included in the new RPT. Otherwise, only the permissions referenced in this
|
||||
* authorization request will be granted in the new RPT.
|
||||
*/
|
||||
incrementalAuthorization?:boolean
|
||||
}
|
||||
|
||||
interface AuthorizationRequestMetadata {
|
||||
/**
|
||||
* A boolean value indicating to the server if resource names should be included in the RPT’s permissions.
|
||||
* If false, only the resource identifier is included.
|
||||
*/
|
||||
responseIncludeResourceName?:any,
|
||||
|
||||
/**
|
||||
* An integer N that defines a limit for the amount of permissions an RPT can have. When used together with
|
||||
* rpt parameter, only the last N requested permissions will be kept in the RPT.
|
||||
*/
|
||||
response_permissions_limit?:number
|
||||
}
|
||||
|
||||
interface ResourcePermission {
|
||||
/**
|
||||
* The id or name of a resource.
|
||||
*/
|
||||
id:string,
|
||||
|
||||
/**
|
||||
* An array of strings where each value is the name of a scope associated with the resource.
|
||||
*/
|
||||
scopes?:string[]
|
||||
}
|
||||
|
||||
interface KeycloakAuthorizationInstance {
|
||||
rpt: any;
|
||||
config: { rpt_endpoint: string };
|
||||
@@ -42,18 +100,23 @@ declare namespace KeycloakAuthorization {
|
||||
init(): void;
|
||||
|
||||
/**
|
||||
* This method enables client applications to better integrate with resource servers protected by a Keycloak
|
||||
* policy enforcer.
|
||||
*
|
||||
* In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
|
||||
* necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
|
||||
* depending on how the policy enforcer at the resource server was configured.
|
||||
*/
|
||||
authorize(wwwAuthenticateHeader: string): KeycloakAuthorizationPromise;
|
||||
* This method enables client applications to better integrate with resource servers protected by a Keycloak
|
||||
* policy enforcer using UMA protocol.
|
||||
*
|
||||
* The authorization request must be provided with a ticket.
|
||||
*
|
||||
* @param authorizationRequest An AuthorizationRequest instance with a valid permission ticket set.
|
||||
* @returns A promise to set functions to be invoked on grant, deny or error.
|
||||
*/
|
||||
authorize(authorizationRequest: AuthorizationRequest): KeycloakAuthorizationPromise;
|
||||
|
||||
/**
|
||||
* Obtains all entitlements from a Keycloak server based on a given resourceServerId.
|
||||
*
|
||||
* @param resourceServerId The id (client id) of the resource server to obtain permissions from.
|
||||
* @param authorizationRequest An AuthorizationRequest instance.
|
||||
* @returns A promise to set functions to be invoked on grant, deny or error.
|
||||
*/
|
||||
entitlement(resourceServerId: string, entitlementRequest: {}): KeycloakAuthorizationPromise;
|
||||
entitlement(resourceServerId: string, authorizationRequest?: AuthorizationRequest): KeycloakAuthorizationPromise;
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,9 @@
|
||||
|
||||
/**
|
||||
* This method enables client applications to better integrate with resource servers protected by a Keycloak
|
||||
* policy enforcer.
|
||||
* policy enforcer using UMA protocol.
|
||||
*
|
||||
* In this case, the resource server will respond with a 401 status code and a WWW-Authenticate header holding the
|
||||
* necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
|
||||
* depending on how the policy enforcer at the resource server was configured.
|
||||
* The authorization request must be provided with a ticket.
|
||||
*/
|
||||
this.authorize = function (authorizationRequest) {
|
||||
this.then = function (onGrant, onDeny, onError) {
|
||||
@@ -205,6 +203,8 @@
|
||||
};
|
||||
|
||||
this.init(this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
|
||||
|
||||
+28
-12
@@ -29,7 +29,7 @@ export = Keycloak;
|
||||
declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
|
||||
|
||||
declare namespace Keycloak {
|
||||
type KeycloakAdapterName = 'cordova'|'default' | any;
|
||||
type KeycloakAdapterName = 'cordova' | 'cordova-native' |'default' | any;
|
||||
type KeycloakOnLoad = 'login-required'|'check-sso';
|
||||
type KeycloakResponseMode = 'query'|'fragment';
|
||||
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
|
||||
@@ -98,6 +98,12 @@ declare namespace Keycloak {
|
||||
*/
|
||||
responseMode?: KeycloakResponseMode;
|
||||
|
||||
/**
|
||||
* Specifies a default uri to redirect to after login or logout.
|
||||
* This is currently supported for adapter 'cordova-native' and 'default'
|
||||
*/
|
||||
redirectUri?: string;
|
||||
|
||||
/**
|
||||
* Set the OpenID Connect flow.
|
||||
* @default standard
|
||||
@@ -161,6 +167,14 @@ declare namespace Keycloak {
|
||||
* the user's profile to a new preferred locale.
|
||||
*/
|
||||
kcLocale?: string;
|
||||
|
||||
/**
|
||||
* Specifies arguments that are passed to the Cordova in-app-browser (if applicable).
|
||||
* Options 'hidden' and 'location' are not affected by these arguments.
|
||||
* All available options are defined at https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/.
|
||||
* Example of use: { zoom: "no", hardwareback: "yes" }
|
||||
*/
|
||||
cordovaOptions?: { [optionName: string]: string };
|
||||
}
|
||||
|
||||
type KeycloakPromiseCallback<T> = (result: T) => void;
|
||||
@@ -202,6 +216,16 @@ declare namespace Keycloak {
|
||||
createdTimestamp?: number;
|
||||
}
|
||||
|
||||
interface KeycloakTokenParsed {
|
||||
exp?: number;
|
||||
iat?: number;
|
||||
nonce?: string;
|
||||
sub?: string;
|
||||
session_state?: string;
|
||||
realm_access?: { roles: string[] };
|
||||
resource_access?: string[];
|
||||
}
|
||||
|
||||
// export interface KeycloakUserInfo {}
|
||||
|
||||
/**
|
||||
@@ -255,15 +279,7 @@ declare namespace Keycloak {
|
||||
/**
|
||||
* The parsed token as a JavaScript object.
|
||||
*/
|
||||
tokenParsed?: {
|
||||
exp?: number;
|
||||
iat?: number;
|
||||
nonce?: string;
|
||||
sub?: string;
|
||||
session_state?: string;
|
||||
realm_access?: { roles: string[] };
|
||||
resource_access?: string[];
|
||||
};
|
||||
tokenParsed?: KeycloakTokenParsed;
|
||||
|
||||
/**
|
||||
* The base64 encoded refresh token that can be used to retrieve a new token.
|
||||
@@ -273,7 +289,7 @@ declare namespace Keycloak {
|
||||
/**
|
||||
* The parsed refresh token as a JavaScript object.
|
||||
*/
|
||||
refreshTokenParsed?: { nonce?: string };
|
||||
refreshTokenParsed?: KeycloakTokenParsed;
|
||||
|
||||
/**
|
||||
* The base64 encoded ID token.
|
||||
@@ -283,7 +299,7 @@ declare namespace Keycloak {
|
||||
/**
|
||||
* The parsed id token as a JavaScript object.
|
||||
*/
|
||||
idTokenParsed?: { nonce?: string };
|
||||
idTokenParsed?: KeycloakTokenParsed;
|
||||
|
||||
/**
|
||||
* The estimated time difference between the browser time and the Keycloak
|
||||
|
||||
@@ -46,11 +46,10 @@
|
||||
kc.authenticated = false;
|
||||
|
||||
callbackStorage = createCallbackStorage();
|
||||
var adapters = ['default', 'cordova', 'cordova-native'];
|
||||
|
||||
if (initOptions && initOptions.adapter === 'cordova') {
|
||||
adapter = loadAdapter('cordova');
|
||||
} else if (initOptions && initOptions.adapter === 'default') {
|
||||
adapter = loadAdapter();
|
||||
if (initOptions && adapters.indexOf(initOptions.adapter) > -1) {
|
||||
adapter = loadAdapter(initOptions.adapter);
|
||||
} else if (initOptions && typeof initOptions.adapter === "object") {
|
||||
adapter = initOptions.adapter;
|
||||
} else {
|
||||
@@ -74,6 +73,12 @@
|
||||
loginIframe.interval = initOptions.checkLoginIframeInterval;
|
||||
}
|
||||
|
||||
if (initOptions.promiseType === 'native') {
|
||||
kc.useNativePromise = typeof Promise === "function";
|
||||
} else {
|
||||
kc.useNativePromise = false;
|
||||
}
|
||||
|
||||
if (initOptions.onLoad === 'login-required') {
|
||||
kc.loginRequired = true;
|
||||
}
|
||||
@@ -106,6 +111,10 @@
|
||||
if (initOptions.timeSkew != null) {
|
||||
kc.timeSkew = initOptions.timeSkew;
|
||||
}
|
||||
|
||||
if(initOptions.redirectUri) {
|
||||
kc.redirectUri = initOptions.redirectUri;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kc.responseMode) {
|
||||
@@ -116,9 +125,9 @@
|
||||
kc.flow = 'standard';
|
||||
}
|
||||
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(false);
|
||||
|
||||
var initPromise = createPromise();
|
||||
var initPromise = createPromise(true);
|
||||
initPromise.promise.success(function() {
|
||||
kc.onReady && kc.onReady(kc.authenticated);
|
||||
promise.setSuccess(kc.authenticated);
|
||||
@@ -145,10 +154,14 @@
|
||||
case 'check-sso':
|
||||
if (loginIframe.enable) {
|
||||
setupCheckLoginIframe().success(function() {
|
||||
checkLoginIframe().success(function () {
|
||||
doLogin(false);
|
||||
checkLoginIframe().success(function (unchanged) {
|
||||
if (!unchanged) {
|
||||
doLogin(false);
|
||||
} else {
|
||||
initPromise.setSuccess();
|
||||
}
|
||||
}).error(function () {
|
||||
initPromise.setSuccess();
|
||||
initPromise.setError();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@@ -167,7 +180,7 @@
|
||||
var callback = parseCallback(window.location.href);
|
||||
|
||||
if (callback) {
|
||||
window.history.replaceState({}, null, callback.newUrl);
|
||||
window.history.replaceState(window.history.state, null, callback.newUrl);
|
||||
}
|
||||
|
||||
if (callback && callback.valid) {
|
||||
@@ -182,12 +195,16 @@
|
||||
|
||||
if (loginIframe.enable) {
|
||||
setupCheckLoginIframe().success(function() {
|
||||
checkLoginIframe().success(function () {
|
||||
kc.onAuthSuccess && kc.onAuthSuccess();
|
||||
initPromise.setSuccess();
|
||||
checkLoginIframe().success(function (unchanged) {
|
||||
if (unchanged) {
|
||||
kc.onAuthSuccess && kc.onAuthSuccess();
|
||||
initPromise.setSuccess();
|
||||
scheduleCheckIframe();
|
||||
} else {
|
||||
initPromise.setSuccess();
|
||||
}
|
||||
}).error(function () {
|
||||
setToken(null, null, null);
|
||||
initPromise.setSuccess();
|
||||
initPromise.setError();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@@ -250,7 +267,16 @@
|
||||
baseUrl = kc.endpoints.authorize();
|
||||
}
|
||||
|
||||
var scope = (options && options.scope) ? "openid " + options.scope : "openid";
|
||||
var scope;
|
||||
if (options && options.scope) {
|
||||
if (options.scope.indexOf("openid") != -1) {
|
||||
scope = options.scope;
|
||||
} else {
|
||||
scope = "openid " + options.scope;
|
||||
}
|
||||
} else {
|
||||
scope = "openid";
|
||||
}
|
||||
|
||||
var url = baseUrl
|
||||
+ '?client_id=' + encodeURIComponent(kc.clientId)
|
||||
@@ -350,7 +376,7 @@
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'bearer ' + kc.token);
|
||||
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(false);
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
@@ -375,7 +401,7 @@
|
||||
req.setRequestHeader('Accept', 'application/json');
|
||||
req.setRequestHeader('Authorization', 'bearer ' + kc.token);
|
||||
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(false);
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4) {
|
||||
@@ -411,7 +437,7 @@
|
||||
}
|
||||
|
||||
kc.updateToken = function(minValidity) {
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(false);
|
||||
|
||||
if (!kc.refreshToken) {
|
||||
promise.setError();
|
||||
@@ -575,6 +601,7 @@
|
||||
|
||||
var tokenResponse = JSON.parse(req.responseText);
|
||||
authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
|
||||
scheduleCheckIframe();
|
||||
} else {
|
||||
kc.onAuthError && kc.onAuthError();
|
||||
promise && promise.setError();
|
||||
@@ -608,7 +635,7 @@
|
||||
}
|
||||
|
||||
function loadConfig(url) {
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(true);
|
||||
var configUrl;
|
||||
|
||||
if (!config) {
|
||||
@@ -898,7 +925,7 @@
|
||||
supportedParams = ['code', 'state', 'session_state'];
|
||||
break;
|
||||
case 'implicit':
|
||||
supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
|
||||
supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in'];
|
||||
break;
|
||||
case 'hybrid':
|
||||
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
|
||||
@@ -967,8 +994,8 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
function createPromise() {
|
||||
if (typeof Promise === "function") {
|
||||
function createPromise(internal) {
|
||||
if (!internal && kc.useNativePromise) {
|
||||
return createNativePromise();
|
||||
} else {
|
||||
return createLegacyPromise();
|
||||
@@ -980,12 +1007,10 @@
|
||||
// interface of the custom promise type previously used by the API
|
||||
var p = {
|
||||
setSuccess: function(result) {
|
||||
p.success = true;
|
||||
p.resolve(result);
|
||||
},
|
||||
|
||||
setError: function(result) {
|
||||
p.success = false;
|
||||
p.reject(result);
|
||||
}
|
||||
};
|
||||
@@ -993,14 +1018,6 @@
|
||||
p.resolve = resolve;
|
||||
p.reject = reject;
|
||||
});
|
||||
p.promise.success = function(callback) {
|
||||
p.promise.then(callback);
|
||||
return p.promise;
|
||||
}
|
||||
p.promise.error = function(callback) {
|
||||
p.promise.catch(callback);
|
||||
return p.promise;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1062,7 @@
|
||||
}
|
||||
|
||||
function setupCheckLoginIframe() {
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(true);
|
||||
|
||||
if (!loginIframe.enable) {
|
||||
promise.setSuccess();
|
||||
@@ -1068,8 +1085,6 @@
|
||||
loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8));
|
||||
}
|
||||
promise.setSuccess();
|
||||
|
||||
setTimeout(check, loginIframe.interval * 1000);
|
||||
}
|
||||
|
||||
var src = kc.endpoints.checkSessionIframe();
|
||||
@@ -1096,31 +1111,38 @@
|
||||
|
||||
for (var i = callbacks.length - 1; i >= 0; --i) {
|
||||
var promise = callbacks[i];
|
||||
if (event.data == 'unchanged') {
|
||||
promise.setSuccess();
|
||||
} else {
|
||||
if (event.data == 'error') {
|
||||
promise.setError();
|
||||
} else {
|
||||
promise.setSuccess(event.data == 'unchanged');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageCallback, false);
|
||||
|
||||
var check = function() {
|
||||
checkLoginIframe();
|
||||
if (kc.token) {
|
||||
setTimeout(check, loginIframe.interval * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
return promise.promise;
|
||||
}
|
||||
|
||||
function scheduleCheckIframe() {
|
||||
if (loginIframe.enable) {
|
||||
if (kc.token) {
|
||||
setTimeout(function() {
|
||||
checkLoginIframe().success(function(unchanged) {
|
||||
if (unchanged) {
|
||||
scheduleCheckIframe();
|
||||
}
|
||||
});
|
||||
}, loginIframe.interval * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkLoginIframe() {
|
||||
var promise = createPromise();
|
||||
var promise = createPromise(true);
|
||||
|
||||
if (loginIframe.iframe && loginIframe.iframeOrigin ) {
|
||||
var msg = kc.clientId + ' ' + kc.sessionId;
|
||||
var msg = kc.clientId + ' ' + (kc.sessionId ? kc.sessionId : '');
|
||||
loginIframe.callbackList.push(promise);
|
||||
var origin = loginIframe.iframeOrigin;
|
||||
if (loginIframe.callbackList.length == 1) {
|
||||
@@ -1137,17 +1159,17 @@
|
||||
if (!type || type == 'default') {
|
||||
return {
|
||||
login: function(options) {
|
||||
window.location.href = kc.createLoginUrl(options);
|
||||
window.location.replace(kc.createLoginUrl(options));
|
||||
return createPromise().promise;
|
||||
},
|
||||
|
||||
logout: function(options) {
|
||||
window.location.href = kc.createLogoutUrl(options);
|
||||
window.location.replace(kc.createLogoutUrl(options));
|
||||
return createPromise().promise;
|
||||
},
|
||||
|
||||
register: function(options) {
|
||||
window.location.href = kc.createRegisterUrl(options);
|
||||
window.location.replace(kc.createRegisterUrl(options));
|
||||
return createPromise().promise;
|
||||
},
|
||||
|
||||
@@ -1158,7 +1180,7 @@
|
||||
} else {
|
||||
throw "Not supported by the OIDC server";
|
||||
}
|
||||
return createPromise().promise;
|
||||
return createPromise(false).promise;
|
||||
},
|
||||
|
||||
redirectUri: function(options, encodeHash) {
|
||||
@@ -1187,24 +1209,54 @@
|
||||
return window.open(loginUrl, target, options);
|
||||
}
|
||||
};
|
||||
|
||||
var shallowCloneCordovaOptions = function (userOptions) {
|
||||
if (userOptions && userOptions.cordovaOptions) {
|
||||
return Object.keys(userOptions.cordovaOptions).reduce(function (options, optionName) {
|
||||
options[optionName] = userOptions.cordovaOptions[optionName];
|
||||
return options;
|
||||
}, {});
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
var formatCordovaOptions = function (cordovaOptions) {
|
||||
return Object.keys(cordovaOptions).reduce(function (options, optionName) {
|
||||
options.push(optionName+"="+cordovaOptions[optionName]);
|
||||
return options;
|
||||
}, []).join(",");
|
||||
};
|
||||
|
||||
var createCordovaOptions = function (userOptions) {
|
||||
var cordovaOptions = shallowCloneCordovaOptions(userOptions);
|
||||
cordovaOptions.location = 'no';
|
||||
if (userOptions && userOptions.prompt == 'none') {
|
||||
cordovaOptions.hidden = 'yes';
|
||||
}
|
||||
return formatCordovaOptions(cordovaOptions);
|
||||
};
|
||||
|
||||
return {
|
||||
login: function(options) {
|
||||
var promise = createPromise();
|
||||
|
||||
var o = 'location=no';
|
||||
if (options && options.prompt == 'none') {
|
||||
o += ',hidden=yes';
|
||||
}
|
||||
var promise = createPromise(false);
|
||||
|
||||
var cordovaOptions = createCordovaOptions(options);
|
||||
var loginUrl = kc.createLoginUrl(options);
|
||||
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
|
||||
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', cordovaOptions);
|
||||
var completed = false;
|
||||
|
||||
var closed = false;
|
||||
var closeBrowser = function() {
|
||||
closed = true;
|
||||
ref.close();
|
||||
};
|
||||
|
||||
ref.addEventListener('loadstart', function(event) {
|
||||
if (event.url.indexOf('http://localhost') == 0) {
|
||||
var callback = parseCallback(event.url);
|
||||
processCallback(callback, promise);
|
||||
ref.close();
|
||||
closeBrowser();
|
||||
completed = true;
|
||||
}
|
||||
});
|
||||
@@ -1214,21 +1266,29 @@
|
||||
if (event.url.indexOf('http://localhost') == 0) {
|
||||
var callback = parseCallback(event.url);
|
||||
processCallback(callback, promise);
|
||||
ref.close();
|
||||
closeBrowser();
|
||||
completed = true;
|
||||
} else {
|
||||
promise.setError();
|
||||
ref.close();
|
||||
closeBrowser();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ref.addEventListener('exit', function(event) {
|
||||
if (!closed) {
|
||||
promise.setError({
|
||||
reason: "closed_by_user"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return promise.promise;
|
||||
},
|
||||
|
||||
logout: function(options) {
|
||||
var promise = createPromise();
|
||||
|
||||
var promise = createPromise(false);
|
||||
|
||||
var logoutUrl = kc.createLogoutUrl(options);
|
||||
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
|
||||
|
||||
@@ -1263,7 +1323,8 @@
|
||||
|
||||
register : function() {
|
||||
var registerUrl = kc.createRegisterUrl();
|
||||
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
|
||||
var cordovaOptions = createCordovaOptions(options);
|
||||
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', cordovaOptions);
|
||||
ref.addEventListener('loadstart', function(event) {
|
||||
if (event.url.indexOf('http://localhost') == 0) {
|
||||
ref.close();
|
||||
@@ -1291,6 +1352,75 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (type == 'cordova-native') {
|
||||
loginIframe.enable = false;
|
||||
|
||||
return {
|
||||
login: function(options) {
|
||||
var promise = createPromise(false);
|
||||
var loginUrl = kc.createLoginUrl(options);
|
||||
|
||||
universalLinks.subscribe('keycloak', function(event) {
|
||||
universalLinks.unsubscribe('keycloak');
|
||||
window.cordova.plugins.browsertab.close();
|
||||
var oauth = parseCallback(event.url);
|
||||
processCallback(oauth, promise);
|
||||
});
|
||||
|
||||
window.cordova.plugins.browsertab.openUrl(loginUrl);
|
||||
return promise.promise;
|
||||
},
|
||||
|
||||
logout: function(options) {
|
||||
var promise = createPromise(false);
|
||||
var logoutUrl = kc.createLogoutUrl(options);
|
||||
|
||||
universalLinks.subscribe('keycloak', function(event) {
|
||||
universalLinks.unsubscribe('keycloak');
|
||||
window.cordova.plugins.browsertab.close();
|
||||
kc.clearToken();
|
||||
promise.setSuccess();
|
||||
});
|
||||
|
||||
window.cordova.plugins.browsertab.openUrl(logoutUrl);
|
||||
return promise.promise;
|
||||
},
|
||||
|
||||
register : function(options) {
|
||||
var promise = createPromise(false);
|
||||
var registerUrl = kc.createRegisterUrl(options);
|
||||
universalLinks.subscribe('keycloak' , function(event) {
|
||||
universalLinks.unsubscribe('keycloak');
|
||||
window.cordova.plugins.browsertab.close();
|
||||
var oauth = parseCallback(event.url);
|
||||
processCallback(oauth, promise);
|
||||
});
|
||||
window.cordova.plugins.browsertab.openUrl(registerUrl);
|
||||
return promise.promise;
|
||||
|
||||
},
|
||||
|
||||
accountManagement : function() {
|
||||
var accountUrl = kc.createAccountUrl();
|
||||
if (typeof accountUrl !== 'undefined') {
|
||||
window.cordova.plugins.browsertab.openUrl(accountUrl);
|
||||
} else {
|
||||
throw "Not supported by the OIDC server";
|
||||
}
|
||||
},
|
||||
|
||||
redirectUri: function(options) {
|
||||
if (options && options.redirectUri) {
|
||||
return options.redirectUri;
|
||||
} else if (kc.redirectUri) {
|
||||
return kc.redirectUri;
|
||||
} else {
|
||||
return "http://localhost";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw 'invalid adapter type: ' + type;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,20 @@
|
||||
function checkState(clientId, origin, sessionState, callback) {
|
||||
var cookie = getCookie();
|
||||
|
||||
if (!cookie) {
|
||||
callback('changed');
|
||||
} else if (!init) {
|
||||
var checkCookie = function() {
|
||||
if (clientId === init.clientId && origin === init.origin) {
|
||||
var c = cookie.split('/');
|
||||
if (sessionState === c[2]) {
|
||||
callback('unchanged');
|
||||
} else {
|
||||
callback('changed');
|
||||
}
|
||||
} else {
|
||||
callback('error');
|
||||
}
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
var req = new XMLHttpRequest();
|
||||
|
||||
var url = location.href.split("?")[0] + "/init";
|
||||
@@ -41,9 +52,15 @@
|
||||
clientId: clientId,
|
||||
origin: origin
|
||||
}
|
||||
callback('unchanged');
|
||||
} else if (req.status === 404) {
|
||||
callback('changed');
|
||||
if (!cookie) {
|
||||
if (sessionState != '') {
|
||||
callback('changed');
|
||||
} else {
|
||||
callback('unchanged');
|
||||
}
|
||||
} else {
|
||||
checkCookie();
|
||||
}
|
||||
} else {
|
||||
callback('error');
|
||||
}
|
||||
@@ -51,17 +68,14 @@
|
||||
};
|
||||
|
||||
req.send();
|
||||
} else {
|
||||
if (clientId === init.clientId && origin === init.origin) {
|
||||
var c = cookie.split('/');
|
||||
if (sessionState === c[2]) {
|
||||
callback('unchanged');
|
||||
} else {
|
||||
callback('changed');
|
||||
}
|
||||
} else if (!cookie) {
|
||||
if (sessionState != '') {
|
||||
callback('changed');
|
||||
} else {
|
||||
callback('error');
|
||||
callback('unchanged');
|
||||
}
|
||||
} else {
|
||||
checkCookie();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -39,14 +39,6 @@
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -31,12 +31,15 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<jetty9.version>8.1.17.v20150415</jetty9.version>
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.osgi.*
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
org.ops4j.pax.web.*;version="[3.0,5)",
|
||||
org.ops4j.pax.web.*;version="[3.0,8)",
|
||||
javax.servlet.*;version="[2.5,4)";resolution:=optional,
|
||||
org.eclipse.jetty.*;version="[8.1,10)";resolution:=optional,
|
||||
org.keycloak.*;version="${project.version}",
|
||||
@@ -77,11 +80,6 @@
|
||||
<artifactId>org.osgi.enterprise</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-runtime</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ops4j.pax.web</groupId>
|
||||
<artifactId>pax-web-api</artifactId>
|
||||
@@ -99,19 +97,16 @@
|
||||
<version>${cxf.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.osgi;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
|
||||
/**
|
||||
* This {@link KeycloakConfigResolver} tries to resolve most specific configuration for given URI path. If not found,
|
||||
* <em>parent</em> path is checked up to top-level path.
|
||||
*/
|
||||
public class HierarchicalPathBasedKeycloakConfigResolver extends PathBasedKeycloakConfigResolver {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(HierarchicalPathBasedKeycloakConfigResolver.class);
|
||||
|
||||
public HierarchicalPathBasedKeycloakConfigResolver() {
|
||||
prepopulateCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
|
||||
// we cached all available deployments initially and now we'll try to check them from
|
||||
// most specific to most general
|
||||
URI uri = URI.create(request.getURI());
|
||||
String path = uri.getPath();
|
||||
if (path != null) {
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
String[] segments = path.split("/");
|
||||
List<String> paths = collectPaths(segments);
|
||||
for (String pathFragment: paths) {
|
||||
KeycloakDeployment cachedDeployment = super.getCachedDeployment(pathFragment);
|
||||
if (cachedDeployment != null) {
|
||||
return cachedDeployment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Can't find Keycloak configuration related to URI path " + uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>For segments like "a, b, c, d", returns:<ul>
|
||||
* <li>"a-b-c-d"</li>
|
||||
* <li>"a-b-c"</li>
|
||||
* <li>"a-b"</li>
|
||||
* <li>"a"</li>
|
||||
* <li>""</li>
|
||||
* </ul></p>
|
||||
* @param segments
|
||||
* @return
|
||||
*/
|
||||
private List<String> collectPaths(String[] segments) {
|
||||
List<String> result = new ArrayList<>(segments.length + 1);
|
||||
for (int idx = segments.length; idx >= 0; idx--) {
|
||||
StringBuilder sb = null;
|
||||
for (int i = 0; i < idx; i++) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
sb.append("-").append(segments[i]);
|
||||
}
|
||||
result.add(sb == null ? "" : sb.toString().substring(1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+135
-22
@@ -16,56 +16,169 @@
|
||||
*/
|
||||
package org.keycloak.adapters.osgi;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
|
||||
|
||||
protected static final Logger log = Logger.getLogger(PathBasedKeycloakConfigResolver.class);
|
||||
|
||||
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>();
|
||||
|
||||
private File keycloakConfigLocation = null;
|
||||
|
||||
public PathBasedKeycloakConfigResolver() {
|
||||
String location = null;
|
||||
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
|
||||
if (keycloakConfig != null && !"".equals(keycloakConfig.trim())) {
|
||||
location = keycloakConfig;
|
||||
} else {
|
||||
String karafEtc = (String) System.getProperties().get("karaf.etc");
|
||||
if (karafEtc != null && !"".equals(karafEtc.trim())) {
|
||||
location = karafEtc;
|
||||
}
|
||||
}
|
||||
if (location != null) {
|
||||
File loc = new File(location);
|
||||
if (loc.isDirectory()) {
|
||||
keycloakConfigLocation = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
|
||||
String path = request.getURI();
|
||||
String[] urlTokens = path.split("/");
|
||||
if (urlTokens.length < 4) {
|
||||
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
|
||||
}
|
||||
String webContext = getDeploymentKeyForURI(request);
|
||||
|
||||
String webContext = urlTokens[3];
|
||||
return getOrCreateDeployment(webContext);
|
||||
}
|
||||
|
||||
KeycloakDeployment deployment = cache.get(webContext);
|
||||
/**
|
||||
* {@code pathFragment} is a key for {@link KeycloakDeployment deployments}. The key is used to construct
|
||||
* a path relative to {@code keycloak.config} or {@code karaf.etc} system properties.
|
||||
* For given key, {@code <key>-keycloak.json} file is checked.
|
||||
* @param pathFragment
|
||||
* @return
|
||||
*/
|
||||
protected synchronized KeycloakDeployment getOrCreateDeployment(String pathFragment) {
|
||||
KeycloakDeployment deployment = getCachedDeployment(pathFragment);
|
||||
if (null == deployment) {
|
||||
// not found on the simple cache, try to load it from the file system
|
||||
String keycloakConfig = (String) System.getProperties().get("keycloak.config");
|
||||
if(keycloakConfig == null || "".equals(keycloakConfig.trim())){
|
||||
String karafEtc = (String) System.getProperties().get("karaf.etc");
|
||||
if(karafEtc == null || "".equals(karafEtc.trim())){
|
||||
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set. Please set one of them.");
|
||||
}
|
||||
keycloakConfig = karafEtc;
|
||||
if (keycloakConfigLocation == null) {
|
||||
throw new IllegalStateException("Neither \"keycloak.config\" nor \"karaf.etc\" java properties are set." +
|
||||
" Please set one of them.");
|
||||
}
|
||||
|
||||
String absolutePath = keycloakConfig + File.separator + webContext + "-keycloak.json";
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(absolutePath);
|
||||
} catch (FileNotFoundException e){
|
||||
throw new IllegalStateException("Not able to find the file " + absolutePath);
|
||||
File configuration = new File(keycloakConfigLocation, pathFragment + ("".equals(pathFragment) ? "" : "-")
|
||||
+ "keycloak.json");
|
||||
if (!cacheConfiguration(pathFragment, configuration)) {
|
||||
throw new IllegalStateException("Not able to read the file " + configuration);
|
||||
}
|
||||
deployment = KeycloakDeploymentBuilder.build(is);
|
||||
cache.put(webContext, deployment);
|
||||
deployment = getCachedDeployment(pathFragment);
|
||||
}
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
protected synchronized KeycloakDeployment getCachedDeployment(String pathFragment) {
|
||||
return cache.get(pathFragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's a need, we can pre populate the cache of deployments.
|
||||
*/
|
||||
protected void prepopulateCache() {
|
||||
if (keycloakConfigLocation == null || !keycloakConfigLocation.isDirectory()) {
|
||||
log.warn("Can't cache Keycloak configurations. No configuration storage is accessible." +
|
||||
" Please set either \"keycloak.config\" or \"karaf.etc\" system properties");
|
||||
return;
|
||||
}
|
||||
|
||||
File[] configs = keycloakConfigLocation.listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return pathname.isFile() && pathname.getName().endsWith("keycloak.json");
|
||||
}
|
||||
});
|
||||
if (configs != null) {
|
||||
for (File config: configs) {
|
||||
String pathFragment = null;
|
||||
if ("keycloak.json".equals(config.getName())) {
|
||||
pathFragment = "";
|
||||
} else if (config.getName().endsWith("-keycloak.json")) {
|
||||
pathFragment = config.getName()
|
||||
.substring(0, config.getName().length() - "-keycloak.json".length());
|
||||
}
|
||||
cacheConfiguration(pathFragment, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean cacheConfiguration(String key, File config) {
|
||||
try {
|
||||
InputStream is = new FileInputStream(config);
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
|
||||
cache.put(key, deployment);
|
||||
return true;
|
||||
} catch (FileNotFoundException | RuntimeException e) {
|
||||
log.warn("Can't cache " + config + ": " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a context path from given {@link HttpFacade.Request}. For default context, first path segment
|
||||
* is returned.
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private String getDeploymentKeyForURI(HttpFacade.Request request) {
|
||||
String uri = request.getURI();
|
||||
String relativePath = request.getRelativePath();
|
||||
String webContext = null;
|
||||
if (relativePath == null || !uri.contains(relativePath)) {
|
||||
String[] urlTokens = uri.split("/");
|
||||
if (urlTokens.length < 4) {
|
||||
throw new IllegalStateException("Not able to determine the web-context to load the correspondent keycloak.json file");
|
||||
}
|
||||
|
||||
webContext = urlTokens[3];
|
||||
} else {
|
||||
URI parsedURI = URI.create(uri);
|
||||
String path = parsedURI.getPath();
|
||||
if (path.contains(relativePath)) {
|
||||
path = path.substring(0, path.indexOf(relativePath));
|
||||
}
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
webContext = path;
|
||||
if ("".equals(webContext)) {
|
||||
path = relativePath;
|
||||
while (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
if (path.contains("/")) {
|
||||
path = path.substring(0, path.indexOf("/"));
|
||||
}
|
||||
webContext = path;
|
||||
}
|
||||
}
|
||||
|
||||
return webContext;
|
||||
}
|
||||
|
||||
}
|
||||
+102
-7
@@ -29,11 +29,12 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Integration with pax-web, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
|
||||
* Integration with pax-web in Fuse 6.3, which allows to inject custom jetty-web.xml configuration from current bundle classpath into {@link WebContainer}
|
||||
* and allows to inject custom security constraint for securing resources by Keycloak.
|
||||
*
|
||||
* <p>It assumes that pax-web {@link WebContainer} is used as implementation of OSGI {@link org.osgi.service.http.HttpService}, which
|
||||
@@ -47,7 +48,7 @@ public class PaxWebIntegrationService {
|
||||
|
||||
private BundleContext bundleContext;
|
||||
private String jettyWebXmlLocation;
|
||||
private List<ConstraintMapping> constraintMappings; // Using jetty constraint mapping just because of compatibility with other fuse services
|
||||
private List<Object> constraintMappings;
|
||||
|
||||
private ServiceTracker webContainerTracker;
|
||||
private HttpContext httpContext;
|
||||
@@ -68,11 +69,11 @@ public class PaxWebIntegrationService {
|
||||
this.jettyWebXmlLocation = jettyWebXmlLocation;
|
||||
}
|
||||
|
||||
public List<ConstraintMapping> getConstraintMappings() {
|
||||
public List<Object> getConstraintMappings() {
|
||||
return constraintMappings;
|
||||
}
|
||||
|
||||
public void setConstraintMappings(List<ConstraintMapping> constraintMappings) {
|
||||
public void setConstraintMappings(List<Object> constraintMappings) {
|
||||
this.constraintMappings = constraintMappings;
|
||||
}
|
||||
|
||||
@@ -120,8 +121,25 @@ public class PaxWebIntegrationService {
|
||||
if (constraintMappings == null) {
|
||||
throw new IllegalStateException("constraintMappings was null!");
|
||||
}
|
||||
for (ConstraintMapping constraintMapping : constraintMappings) {
|
||||
addConstraintMapping(service, constraintMapping);
|
||||
List<ConstraintHandler> handlers = new ArrayList<>();
|
||||
try {
|
||||
handlers.add(new JettyConstraintHandler());
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
try {
|
||||
handlers.add(new PaxWebConstraintHandler());
|
||||
} catch (Throwable t) {
|
||||
// Ignore
|
||||
}
|
||||
for (Object constraintMapping : constraintMappings) {
|
||||
boolean handled = false;
|
||||
for (ConstraintHandler handler : handlers) {
|
||||
handled |= handler.addConstraintMapping(httpContext, service, constraintMapping);
|
||||
}
|
||||
if (!handled) {
|
||||
log.warnv("Unable to add constraint mapping for constraint of type " + constraintMapping.getClass().toString());
|
||||
}
|
||||
}
|
||||
|
||||
service.registerLoginConfig("BASIC", "does-not-matter", null, null, httpContext);
|
||||
@@ -146,6 +164,16 @@ public class PaxWebIntegrationService {
|
||||
}
|
||||
}
|
||||
|
||||
protected void addConstraintMapping(WebContainer service, PaxWebSecurityConstraintMapping constraintMapping) {
|
||||
String name = constraintMapping.getConstraintName();
|
||||
if (name == null) {
|
||||
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
|
||||
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
|
||||
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
|
||||
}
|
||||
|
||||
protected void addConstraintMapping(WebContainer service, ConstraintMapping constraintMapping) {
|
||||
Constraint constraint = constraintMapping.getConstraint();
|
||||
String[] roles = constraint.getRoles();
|
||||
@@ -178,4 +206,71 @@ public class PaxWebIntegrationService {
|
||||
service.unregisterConstraintMapping(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface ConstraintHandler {
|
||||
boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm);
|
||||
}
|
||||
|
||||
private static class PaxWebConstraintHandler implements ConstraintHandler {
|
||||
|
||||
@Override
|
||||
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
|
||||
if (cm instanceof PaxWebSecurityConstraintMapping) {
|
||||
PaxWebSecurityConstraintMapping constraintMapping = (PaxWebSecurityConstraintMapping) cm;
|
||||
String name = constraintMapping.getConstraintName();
|
||||
if (name == null) {
|
||||
name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
}
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getUrl() + ", dataConstraint=" + constraintMapping.getDataConstraint() + ", canAuthenticate="
|
||||
+ constraintMapping.isAuthentication() + ", roles=" + constraintMapping.getRoles());
|
||||
service.registerConstraintMapping(name, constraintMapping.getUrl(), constraintMapping.getMapping(), constraintMapping.getDataConstraint(), constraintMapping.isAuthentication(), constraintMapping.getRoles(), httpContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class JettyConstraintHandler implements ConstraintHandler {
|
||||
|
||||
@Override
|
||||
public boolean addConstraintMapping(HttpContext httpContext, WebContainer service, Object cm) {
|
||||
if (cm instanceof ConstraintMapping) {
|
||||
ConstraintMapping constraintMapping = (ConstraintMapping) cm;
|
||||
Constraint constraint = constraintMapping.getConstraint();
|
||||
String[] roles = constraint.getRoles();
|
||||
// name property is unavailable on constraint object :/
|
||||
|
||||
String name = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
|
||||
int dataConstraint = constraint.getDataConstraint();
|
||||
String dataConstraintStr;
|
||||
switch (dataConstraint) {
|
||||
case Constraint.DC_UNSET:
|
||||
dataConstraintStr = null;
|
||||
break;
|
||||
case Constraint.DC_NONE:
|
||||
dataConstraintStr = "NONE";
|
||||
break;
|
||||
case Constraint.DC_CONFIDENTIAL:
|
||||
dataConstraintStr = "CONFIDENTIAL";
|
||||
break;
|
||||
case Constraint.DC_INTEGRAL:
|
||||
dataConstraintStr = "INTEGRAL";
|
||||
break;
|
||||
default:
|
||||
log.warnv("Unknown data constraint: " + dataConstraint);
|
||||
dataConstraintStr = "CONFIDENTIAL";
|
||||
}
|
||||
List<String> rolesList = Arrays.asList(roles);
|
||||
|
||||
log.debug("Adding security constraint name=" + name + ", url=" + constraintMapping.getPathSpec() + ", dataConstraint=" + dataConstraintStr + ", canAuthenticate="
|
||||
+ constraint.getAuthenticate() + ", roles=" + rolesList);
|
||||
service.registerConstraintMapping(name, constraintMapping.getPathSpec(), null, dataConstraintStr, constraint.getAuthenticate(), rolesList, httpContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.keycloak.adapters.osgi;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class PaxWebSecurityConstraintMapping {
|
||||
|
||||
private String constraintName = "Constraint-" + new SecureRandom().nextInt(Integer.MAX_VALUE);
|
||||
private String mapping;
|
||||
private String url;
|
||||
private String dataConstraint = "NONE";
|
||||
private boolean authentication = true;
|
||||
private List<String> roles = new LinkedList<>();
|
||||
|
||||
public String getConstraintName() {
|
||||
return constraintName;
|
||||
}
|
||||
|
||||
public void setConstraintName(String constraintName) {
|
||||
this.constraintName = constraintName;
|
||||
}
|
||||
|
||||
public String getMapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public void setMapping(String mapping) {
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getDataConstraint() {
|
||||
return dataConstraint;
|
||||
}
|
||||
|
||||
public void setDataConstraint(String dataConstraint) {
|
||||
this.dataConstraint = dataConstraint;
|
||||
}
|
||||
|
||||
public boolean isAuthentication() {
|
||||
return authentication;
|
||||
}
|
||||
|
||||
public void setAuthentication(boolean authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public List<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(List<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.osgi;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class HierarchicalPathBasedKeycloakConfigResolverTest {
|
||||
|
||||
@Test
|
||||
public void genericAndSpecificConfigurations() throws Exception {
|
||||
HierarchicalPathBasedKeycloakConfigResolver resolver = new HierarchicalPathBasedKeycloakConfigResolver();
|
||||
populate(resolver, true);
|
||||
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/e?a=b")).getRealm(), equalTo("a-b-c-d-e"));
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/d/x?a=b")).getRealm(), equalTo("a-b-c-d"));
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/c/x/x?a=b")).getRealm(), equalTo("a-b-c"));
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/a/b/x/x/x?a=b")).getRealm(), equalTo("a-b"));
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/a/x/x/x/x?a=b")).getRealm(), equalTo("a"));
|
||||
assertThat(resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b")).getRealm(), equalTo(""));
|
||||
|
||||
populate(resolver, false);
|
||||
try {
|
||||
resolver.resolve(new MockRequest("http://localhost/x/x/x/x/x?a=b"));
|
||||
fail("Expected java.lang.IllegalStateException: Can't find Keycloak configuration ...");
|
||||
} catch (IllegalStateException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, boolean fallback)
|
||||
throws Exception {
|
||||
Field f = PathBasedKeycloakConfigResolver.class.getDeclaredField("cache");
|
||||
f.setAccessible(true);
|
||||
Map<String, KeycloakDeployment> cache = (Map<String, KeycloakDeployment>) f.get(resolver);
|
||||
cache.clear();
|
||||
cache.put("a-b-c-d-e", newKeycloakDeployment("a-b-c-d-e"));
|
||||
cache.put("a-b-c-d", newKeycloakDeployment("a-b-c-d"));
|
||||
cache.put("a-b-c", newKeycloakDeployment("a-b-c"));
|
||||
cache.put("a-b", newKeycloakDeployment("a-b"));
|
||||
cache.put("a", newKeycloakDeployment("a"));
|
||||
if (fallback) {
|
||||
cache.put("", newKeycloakDeployment(""));
|
||||
}
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private KeycloakDeployment newKeycloakDeployment(String realm) {
|
||||
KeycloakDeployment deployment = new KeycloakDeployment();
|
||||
deployment.setRealm(realm);
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
private class MockRequest implements OIDCHttpFacade.Request {
|
||||
|
||||
private String uri;
|
||||
|
||||
public MockRequest(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstParam(String param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryParamValue(String param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFacade.Cookie getCookie(String cookieName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHeaders(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(boolean buffered) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddr() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.osgi;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class PathBasedKeycloakConfigResolverTest {
|
||||
|
||||
@Test
|
||||
public void relativeURIsAndContexts() throws Exception {
|
||||
PathBasedKeycloakConfigResolver resolver = new PathBasedKeycloakConfigResolver();
|
||||
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/a/b")));
|
||||
|
||||
// means default context and actually we use first segment
|
||||
assertNotNull(populate(resolver, "test")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/test/a/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "test/a")
|
||||
.resolve(new MockRequest("http://localhost/test/a/b/c?d=e", "/b/c")));
|
||||
|
||||
assertNotNull(populate(resolver, "")
|
||||
.resolve(new MockRequest("http://localhost/", "/")));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PathBasedKeycloakConfigResolver populate(PathBasedKeycloakConfigResolver resolver, String context)
|
||||
throws Exception {
|
||||
Field f = PathBasedKeycloakConfigResolver.class.getDeclaredField("cache");
|
||||
f.setAccessible(true);
|
||||
Map<String, KeycloakDeployment> cache = (Map<String, KeycloakDeployment>) f.get(resolver);
|
||||
cache.clear();
|
||||
cache.put(context, new KeycloakDeployment());
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private class MockRequest implements OIDCHttpFacade.Request {
|
||||
|
||||
private String uri;
|
||||
private String relativePath;
|
||||
|
||||
public MockRequest(String uri, String relativePath) {
|
||||
this.uri = uri;
|
||||
this.relativePath = relativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath() {
|
||||
return this.relativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstParam(String param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryParamValue(String param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFacade.Cookie getCookie(String cookieName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getHeaders(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(boolean buffered) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteAddr() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak OIDC Client Adapter Modules</name>
|
||||
@@ -34,6 +34,7 @@
|
||||
<module>adapter-core</module>
|
||||
<module>as7-eap6</module>
|
||||
<module>installed</module>
|
||||
<module>fuse7</module>
|
||||
<module>kcinit</module>
|
||||
<module>jaxrs-oauth-client</module>
|
||||
<module>jetty</module>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -30,11 +30,24 @@
|
||||
<name>Keycloak Servlet Filter Adapter Integration</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<keycloak.osgi.export>
|
||||
org.keycloak.adapters.servlet.*
|
||||
</keycloak.osgi.export>
|
||||
<keycloak.osgi.import>
|
||||
javax.servlet.*;version="[2.5,4)";resolution:=optional,
|
||||
org.keycloak.*;version="${project.version}",
|
||||
*;resolution:=optional
|
||||
</keycloak.osgi.import>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -86,12 +99,36 @@
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-ClassPath>.</Bundle-ClassPath>
|
||||
<Bundle-Name>${project.name}</Bundle-Name>
|
||||
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -30,6 +30,11 @@
|
||||
<name>Keycloak Servlet OAuth Client</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
@@ -74,7 +79,6 @@
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -83,17 +87,4 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -105,12 +105,4 @@
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+32
-20
@@ -25,27 +25,17 @@ import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
|
||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.undertow.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -65,6 +55,10 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
KeycloakSpringBootConfigResolverWrapper.setApplicationContext(context);
|
||||
}
|
||||
|
||||
static class KeycloakBaseUndertowDeploymentInfoCustomizer {
|
||||
|
||||
@@ -81,7 +75,7 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
deploymentInfo.setLoginConfig(loginConfig);
|
||||
|
||||
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolverWrapper.class.getName());
|
||||
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
|
||||
|
||||
deploymentInfo.addServletExtension(new KeycloakServletExtension());
|
||||
@@ -123,7 +117,7 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
public void customize(Server server) {
|
||||
|
||||
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
|
||||
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
|
||||
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolverWrapper());
|
||||
|
||||
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
|
||||
on how to map servlet spec to Constraints */
|
||||
@@ -187,7 +181,7 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
WebAppContext webAppContext = server.getBean(WebAppContext.class);
|
||||
//if not found as registered bean let's try the handler
|
||||
if(webAppContext==null){
|
||||
webAppContext = (WebAppContext) server.getHandler();
|
||||
webAppContext = getWebAppContext(server.getHandlers());
|
||||
}
|
||||
|
||||
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||
@@ -196,6 +190,21 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
webAppContext.setSecurityHandler(securityHandler);
|
||||
}
|
||||
|
||||
private WebAppContext getWebAppContext(Handler... handlers) {
|
||||
for (Handler handler : handlers) {
|
||||
if (handler instanceof WebAppContext) {
|
||||
return (WebAppContext) handler;
|
||||
} else if (handler instanceof HandlerList) {
|
||||
return getWebAppContext(((HandlerList) handler).getHandlers());
|
||||
} else if (handler instanceof HandlerCollection) {
|
||||
return getWebAppContext(((HandlerCollection) handler).getHandlers());
|
||||
} else if (handler instanceof HandlerWrapper) {
|
||||
return getWebAppContext(((HandlerWrapper) handler).getHandlers());
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("No WebAppContext found in Jetty server handlers");
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakBaseTomcatContextCustomizer {
|
||||
@@ -223,9 +232,12 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||
|
||||
for (String authRole : constraint.getAuthRoles()) {
|
||||
tomcatConstraint.addAuthRole(authRole);
|
||||
if(authRole.equals("*") || authRole.equals("**")) {
|
||||
// For some reasons embed tomcat don't set the auth constraint on true when wildcard is used
|
||||
tomcatConstraint.setAuthConstraint(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||
@@ -256,7 +268,7 @@ public class KeycloakBaseSpringBootConfiguration {
|
||||
context.addConstraint(tomcatConstraint);
|
||||
}
|
||||
|
||||
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolverWrapper.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* <p>A specific implementation of {@link KeycloakSpringConfigResolverWrapper} that first tries to register any {@link KeycloakConfigResolver}
|
||||
* instance provided by the application. if none is provided, {@link KeycloakSpringBootConfigResolver} is set.
|
||||
*
|
||||
* <p>This implementation is specially useful when using Spring Boot and Spring Security in the same application where the same {@link KeycloakConfigResolver}
|
||||
* instance must be used across the different stacks.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class KeycloakSpringBootConfigResolverWrapper extends KeycloakSpringConfigResolverWrapper {
|
||||
|
||||
private static ApplicationContext context;
|
||||
|
||||
public KeycloakSpringBootConfigResolverWrapper() {
|
||||
super(new KeycloakSpringBootConfigResolver());
|
||||
try {
|
||||
setDelegate(context.getBean(KeycloakConfigResolver.class));
|
||||
} catch (NoSuchBeanDefinitionException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setApplicationContext(ApplicationContext context) {
|
||||
KeycloakSpringBootConfigResolverWrapper.context = context;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-boot-container-bundle</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -31,6 +31,8 @@
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<spring-boot.version>1.4.0.RELEASE</spring-boot.version>
|
||||
<spring.version>4.1.6.RELEASE</spring.version>
|
||||
<mockito.version>1.9.5</mockito.version>
|
||||
@@ -125,16 +127,5 @@
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -31,7 +31,7 @@
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>2.0.0.RELEASE</spring-boot.version>
|
||||
<spring-boot.version>2.0.3.RELEASE</spring-boot.version>
|
||||
<spring.version>5.0.2.RELEASE</spring.version>
|
||||
<mockito.version>1.9.5</mockito.version>
|
||||
</properties>
|
||||
@@ -67,13 +67,11 @@
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -140,12 +138,4 @@
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -72,19 +72,16 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${apache-httpcomponents.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.keycloak.adapters.springsecurity.authentication;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RequestAuthenticator;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
|
||||
/**
|
||||
* Creates {@link RequestAuthenticator}s.
|
||||
*/
|
||||
public interface RequestAuthenticatorFactory {
|
||||
/**
|
||||
* Creates new {@link RequestAuthenticator} instances on a per-request basis.
|
||||
*/
|
||||
RequestAuthenticator createRequestAuthenticator(HttpFacade facade, HttpServletRequest request,
|
||||
KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.keycloak.adapters.springsecurity.authentication;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RequestAuthenticator;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
|
||||
public class SpringSecurityRequestAuthenticatorFactory implements RequestAuthenticatorFactory {
|
||||
|
||||
@Override
|
||||
public RequestAuthenticator createRequestAuthenticator(HttpFacade facade,
|
||||
HttpServletRequest request, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort) {
|
||||
return new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, sslRedirectPort);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.adapters.springsecurity.config;
|
||||
|
||||
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
|
||||
/**
|
||||
* Spring applications may use different security stacks in order to enforce access based on the configuration provided
|
||||
* by a {@code KeycloakDeployment}. This implementation of {@code KeycloakConfigResolver} wraps and avoid calling multiple
|
||||
* {@code KeycloakConfigResolver} instances but only those defined by applications or set as default by the configuration.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class KeycloakSpringConfigResolverWrapper implements KeycloakConfigResolver {
|
||||
|
||||
private KeycloakConfigResolver delegate;
|
||||
|
||||
public KeycloakSpringConfigResolverWrapper(KeycloakConfigResolver delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakDeployment resolve(HttpFacade.Request facade) {
|
||||
return delegate.resolve(facade);
|
||||
}
|
||||
|
||||
protected void setDelegate(KeycloakConfigResolver delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -65,7 +65,7 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
protected AdapterDeploymentContext adapterDeploymentContext() throws Exception {
|
||||
AdapterDeploymentContextFactoryBean factoryBean;
|
||||
if (keycloakConfigResolver != null) {
|
||||
factoryBean = new AdapterDeploymentContextFactoryBean(keycloakConfigResolver);
|
||||
factoryBean = new AdapterDeploymentContextFactoryBean(new KeycloakSpringConfigResolverWrapper(keycloakConfigResolver));
|
||||
}
|
||||
else {
|
||||
factoryBean = new AdapterDeploymentContextFactoryBean(keycloakConfigFileResource);
|
||||
@@ -94,11 +94,6 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
return new KeycloakPreAuthActionsFilter(httpSessionManager());
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsFilter() {
|
||||
return new KeycloakAuthenticatedActionsFilter();
|
||||
}
|
||||
|
||||
protected KeycloakCsrfRequestMatcher keycloakCsrfRequestMatcher() {
|
||||
return new KeycloakCsrfRequestMatcher();
|
||||
}
|
||||
@@ -125,8 +120,8 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
.and()
|
||||
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
|
||||
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
|
||||
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
|
||||
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
|
||||
.addFilterAfter(keycloakAuthenticatedActionsRequestFilter(), KeycloakSecurityContextRequestFilter.class)
|
||||
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
|
||||
.and()
|
||||
.logout()
|
||||
@@ -139,4 +134,9 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
protected KeycloakSecurityContextRequestFilter keycloakSecurityContextRequestFilter() {
|
||||
return new KeycloakSecurityContextRequestFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsRequestFilter() {
|
||||
return new KeycloakAuthenticatedActionsFilter();
|
||||
}
|
||||
}
|
||||
+55
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* Copyright 2018 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -14,7 +14,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.springsecurity.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -26,65 +25,84 @@ import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AuthenticatedActionsHandler;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
/**
|
||||
* Exposes a Keycloak adapter {@link AuthenticatedActionsHandler} as a Spring Security filter.
|
||||
*
|
||||
* @author <a href="mailto:srossillo@smartling.com">Scott Rossillo</a>
|
||||
* @version $Revision: 1 $
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class KeycloakAuthenticatedActionsFilter extends GenericFilterBean implements ApplicationContextAware {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticatedActionsFilter.class);
|
||||
private static final String FILTER_APPLIED = KeycloakAuthenticatedActionsFilter.class.getPackage().getName() + ".authenticated-actions";
|
||||
|
||||
private final NodesRegistrationManagement management = new NodesRegistrationManagement();
|
||||
private ApplicationContext applicationContext;
|
||||
private AdapterDeploymentContext deploymentContext;
|
||||
|
||||
|
||||
public KeycloakAuthenticatedActionsFilter() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilterBean() throws ServletException {
|
||||
deploymentContext = applicationContext.getBean(AdapterDeploymentContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
log.debug("Unregistering deployment");
|
||||
management.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest)request, (HttpServletResponse)response);
|
||||
AuthenticatedActionsHandler handler = new AuthenticatedActionsHandler(deploymentContext.resolveDeployment(facade), (OIDCHttpFacade)facade);
|
||||
boolean handled = handler.handledRequest();
|
||||
if (handled) {
|
||||
log.debug("Authenticated filter handled request: {}", ((HttpServletRequest) request).getRequestURI());
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (request.getAttribute(FILTER_APPLIED) != null) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
|
||||
|
||||
KeycloakSecurityContext keycloakSecurityContext = getKeycloakPrincipal();
|
||||
|
||||
if (keycloakSecurityContext instanceof RefreshableKeycloakSecurityContext) {
|
||||
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest) request, (HttpServletResponse) response);
|
||||
KeycloakDeployment deployment = resolveDeployment(request, response);
|
||||
AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, OIDCHttpFacade.class.cast(facade));
|
||||
if (actions.handledRequest()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilterBean() {
|
||||
deploymentContext = applicationContext.getBean(AdapterDeploymentContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private KeycloakSecurityContext getKeycloakPrincipal() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication != null) {
|
||||
Object principal = authentication.getPrincipal();
|
||||
|
||||
if (principal instanceof KeycloakPrincipal) {
|
||||
return KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private KeycloakDeployment resolveDeployment(ServletRequest servletRequest, ServletResponse servletResponse) {
|
||||
return deploymentContext.resolveDeployment(new SimpleHttpFacade(HttpServletRequest.class.cast(servletRequest), HttpServletResponse.class.cast(servletResponse)));
|
||||
}
|
||||
|
||||
private void clearAuthenticationContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -34,7 +34,8 @@ import org.keycloak.adapters.spi.AuthOutcome;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler;
|
||||
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
|
||||
import org.keycloak.adapters.springsecurity.authentication.RequestAuthenticatorFactory;
|
||||
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticatorFactory;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
@@ -83,6 +84,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
private AdapterDeploymentContext adapterDeploymentContext;
|
||||
private AdapterTokenStoreFactory adapterTokenStoreFactory = new SpringSecurityAdapterTokenStoreFactory();
|
||||
private AuthenticationManager authenticationManager;
|
||||
private RequestAuthenticatorFactory requestAuthenticatorFactory = new SpringSecurityRequestAuthenticatorFactory();
|
||||
|
||||
/**
|
||||
* Creates a new Keycloak authentication processing filter with given {@link AuthenticationManager} and the
|
||||
@@ -142,7 +144,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
|
||||
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
|
||||
RequestAuthenticator authenticator
|
||||
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||
= requestAuthenticatorFactory.createRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||
|
||||
AuthOutcome result = authenticator.authenticate();
|
||||
log.debug("Auth outcome: {}", result);
|
||||
@@ -249,4 +251,14 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
public final void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
|
||||
throw new UnsupportedOperationException("This filter does not support explicitly setting a continue chain before success policy");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request authenticator factory to use when creating per-request authenticators.
|
||||
*
|
||||
* @param requestAuthenticatorFactory the <code>RequestAuthenticatorFactory</code> to use
|
||||
*/
|
||||
public void setRequestAuthenticatorFactory(RequestAuthenticatorFactory requestAuthenticatorFactory) {
|
||||
Assert.notNull(requestAuthenticatorFactory, "RequestAuthenticatorFactory cannot be null");
|
||||
this.requestAuthenticatorFactory = requestAuthenticatorFactory;
|
||||
}
|
||||
}
|
||||
+24
-3
@@ -48,10 +48,11 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(KeycloakPreAuthActionsFilter.class);
|
||||
|
||||
private final NodesRegistrationManagement management = new NodesRegistrationManagement();
|
||||
private NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
private ApplicationContext applicationContext;
|
||||
private AdapterDeploymentContext deploymentContext;
|
||||
private UserSessionManagement userSessionManagement;
|
||||
private PreAuthActionsHandlerFactory preAuthActionsHandlerFactory = new PreAuthActionsHandlerFactory();
|
||||
|
||||
public KeycloakPreAuthActionsFilter() {
|
||||
super();
|
||||
@@ -69,7 +70,7 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
|
||||
@Override
|
||||
public void destroy() {
|
||||
log.debug("Unregistering deployment");
|
||||
management.stop();
|
||||
nodesRegistrationManagement.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,7 +78,8 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpFacade facade = new SimpleHttpFacade((HttpServletRequest)request, (HttpServletResponse)response);
|
||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
|
||||
nodesRegistrationManagement.tryRegister(deploymentContext.resolveDeployment(facade));
|
||||
PreAuthActionsHandler handler = preAuthActionsHandlerFactory.createPreAuthActionsHandler(facade);
|
||||
if (handler.handleRequest()) {
|
||||
log.debug("Pre-auth filter handled request: {}", ((HttpServletRequest) request).getRequestURI());
|
||||
} else {
|
||||
@@ -93,4 +95,23 @@ public class KeycloakPreAuthActionsFilter extends GenericFilterBean implements A
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
void setNodesRegistrationManagement(NodesRegistrationManagement nodesRegistrationManagement) {
|
||||
this.nodesRegistrationManagement = nodesRegistrationManagement;
|
||||
}
|
||||
|
||||
void setPreAuthActionsHandlerFactory(PreAuthActionsHandlerFactory preAuthActionsHandlerFactory) {
|
||||
this.preAuthActionsHandlerFactory = preAuthActionsHandlerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link PreAuthActionsHandler}s.
|
||||
*
|
||||
* Package-private class to enable mocking.
|
||||
*/
|
||||
class PreAuthActionsHandlerFactory {
|
||||
PreAuthActionsHandler createPreAuthActionsHandler(HttpFacade facade) {
|
||||
return new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -63,13 +63,15 @@ public class KeycloakSecurityContextRequestFilter extends GenericFilterBean impl
|
||||
RefreshableKeycloakSecurityContext refreshableSecurityContext = (RefreshableKeycloakSecurityContext) keycloakSecurityContext;
|
||||
KeycloakDeployment deployment = resolveDeployment(request, response);
|
||||
|
||||
if (deployment.isAlwaysRefreshToken()) {
|
||||
if (!refreshableSecurityContext.isActive() || deployment.isAlwaysRefreshToken()) {
|
||||
if (refreshableSecurityContext.refreshExpiredToken(false)) {
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), refreshableSecurityContext);
|
||||
} else {
|
||||
clearAuthenticationContext();
|
||||
}
|
||||
}
|
||||
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package org.keycloak.adapters.springsecurity.filter;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.UserSessionManagement;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.PreAuthActionsHandlerFactory;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public class KeycloakPreAuthActionsFilterTest {
|
||||
|
||||
private KeycloakPreAuthActionsFilter filter;
|
||||
|
||||
@Mock
|
||||
private NodesRegistrationManagement nodesRegistrationManagement;
|
||||
@Mock
|
||||
private ApplicationContext applicationContext;
|
||||
@Mock
|
||||
private AdapterDeploymentContext deploymentContext;
|
||||
@Mock
|
||||
private PreAuthActionsHandlerFactory preAuthActionsHandlerFactory;
|
||||
@Mock
|
||||
private UserSessionManagement userSessionManagement;
|
||||
@Mock
|
||||
private PreAuthActionsHandler preAuthActionsHandler;
|
||||
@Mock
|
||||
private KeycloakDeployment deployment;
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
@Mock
|
||||
private FilterChain chain;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
initMocks(this);
|
||||
filter = new KeycloakPreAuthActionsFilter(userSessionManagement);
|
||||
filter.setNodesRegistrationManagement(nodesRegistrationManagement);
|
||||
filter.setApplicationContext(applicationContext);
|
||||
filter.setPreAuthActionsHandlerFactory(preAuthActionsHandlerFactory);
|
||||
when(applicationContext.getBean(AdapterDeploymentContext.class)).thenReturn(deploymentContext);
|
||||
when(deploymentContext.resolveDeployment(any(HttpFacade.class))).thenReturn(deployment);
|
||||
when(preAuthActionsHandlerFactory.createPreAuthActionsHandler(any(HttpFacade.class))).thenReturn(preAuthActionsHandler);
|
||||
filter.initFilterBean();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIgnoreChainWhenPreAuthActionHandlerHandled() throws Exception {
|
||||
when(preAuthActionsHandler.handleRequest()).thenReturn(true);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verifyZeroInteractions(chain);
|
||||
verify(nodesRegistrationManagement).tryRegister(deployment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldContinueChainWhenPreAuthActionHandlerDidNotHandle() throws Exception {
|
||||
when(preAuthActionsHandler.handleRequest()).thenReturn(false);
|
||||
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
verify(chain).doFilter(request, response);;
|
||||
verify(nodesRegistrationManagement).tryRegister(deployment);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
filter.destroy();
|
||||
verify(nodesRegistrationManagement).stop();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Tomcat Integration</name>
|
||||
|
||||
@@ -14,36 +14,38 @@
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.0.0.Beta2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>4.7.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-tomcat-core-adapter</artifactId>
|
||||
<name>Keycloak Tomcat Core Integration</name>
|
||||
<artifactId>keycloak-tomcat-core-adapter</artifactId>
|
||||
<name>Keycloak Tomcat Core Integration</name>
|
||||
<properties>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
|
||||
<tomcat.version>6.0.41</tomcat.version>
|
||||
</properties>
|
||||
<description />
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-spi</artifactId>
|
||||
</dependency>
|
||||
@@ -51,69 +53,57 @@
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-tomcat-adapter-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Authorization -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-client</artifactId>
|
||||
</dependency>
|
||||
<!-- Authorization -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-servlet-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>catalina</artifactId>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-servlet-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>catalina</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+2
-2
@@ -93,7 +93,7 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
CookieTokenStore.removeCookie(facade);
|
||||
CookieTokenStore.removeCookie(deployment, facade);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,7 +132,7 @@ public class CatalinaCookieTokenStore implements AdapterTokenStore {
|
||||
log.fine("Cleanup and expire cookie for user " + principal.getName() + " after failed refresh");
|
||||
request.setUserPrincipal(null);
|
||||
request.setAuthType(null);
|
||||
CookieTokenStore.removeCookie(facade);
|
||||
CookieTokenStore.removeCookie(deployment, facade);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 3162 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user