Compare commits
729
Commits
3.2.1.Final
...
3.4.1.CR1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b069ba27f0 | ||
|
|
4daf6aaec5 | ||
|
|
6bb04617a6 | ||
|
|
7b03eed9c8 | ||
|
|
b466f4d0b6 | ||
|
|
b8e5fd2b99 | ||
|
|
b72b01bb9d | ||
|
|
603052c594 | ||
|
|
106599b52c | ||
|
|
17748d5ba8 | ||
|
|
4caa40fd0a | ||
|
|
674fb31a2c | ||
|
|
c5d9301951 | ||
|
|
5f43a6a342 | ||
|
|
6587cfa084 | ||
|
|
6a528a3ee6 | ||
|
|
2be78a0239 | ||
|
|
4408cdb5c7 | ||
|
|
a3288d5f8e | ||
|
|
9cae8b79e4 | ||
|
|
0bd2e63162 | ||
|
|
9ffc11d04f | ||
|
|
af66c5dbd2 | ||
|
|
83136c1bd8 | ||
|
|
d22c58ee30 | ||
|
|
de6301e65a | ||
|
|
c02c07d78e | ||
|
|
f48509c32c | ||
|
|
c5b06f23e9 | ||
|
|
fc8a8b1dd8 | ||
|
|
bf73375a5c | ||
|
|
4240295af9 | ||
|
|
3afb635110 | ||
|
|
c36b3cd296 | ||
|
|
683fe6fa03 | ||
|
|
0a8995efc7 | ||
|
|
c398f6619f | ||
|
|
33c38ceb8b | ||
|
|
b0b28e6282 | ||
|
|
792ffdf39b | ||
|
|
36314c51d6 | ||
|
|
24b7e318ca | ||
|
|
cf485c3fc9 | ||
|
|
5666bfe88b | ||
|
|
27268044ae | ||
|
|
c860ca61c3 | ||
|
|
1782d220c7 | ||
|
|
c35c6e6ab7 | ||
|
|
0e2332196d | ||
|
|
a8bcdfb401 | ||
|
|
96e481fe45 | ||
|
|
87b17a6e6c | ||
|
|
6035e5c831 | ||
|
|
9d35891e7d | ||
|
|
00677a6b92 | ||
|
|
697caaa805 | ||
|
|
fda491a966 | ||
|
|
92504e8ba3 | ||
|
|
bb900f9db8 | ||
|
|
819a60932e | ||
|
|
e4a6ee19d1 | ||
|
|
aef682cd5b | ||
|
|
dd6502013e | ||
|
|
ecbf6e5386 | ||
|
|
2721e6a5e4 | ||
|
|
e3c9fa25a3 | ||
|
|
6d91ab674b | ||
|
|
6f4ab8870e | ||
|
|
0b3ae30473 | ||
|
|
94ba85c210 | ||
|
|
2117db5e6d | ||
|
|
116bfb05c2 | ||
|
|
aee6d16f58 | ||
|
|
ae29e36e1f | ||
|
|
02220558e6 | ||
|
|
12bdf48ecc | ||
|
|
bd1072d2eb | ||
|
|
61c5a332b4 | ||
|
|
75d517a1e8 | ||
|
|
8993ca08ad | ||
|
|
afd906b9a9 | ||
|
|
c083c1c4cf | ||
|
|
d78bc37253 | ||
|
|
d0ee1e6ee5 | ||
|
|
c16c02b0cf | ||
|
|
7fd237b40b | ||
|
|
a993f6fb75 | ||
|
|
5f20df00d0 | ||
|
|
186b10ab53 | ||
|
|
06762ba13d | ||
|
|
08ca03051c | ||
|
|
8f82440103 | ||
|
|
9485a63157 | ||
|
|
37b625fd99 | ||
|
|
8e53ccf5ab | ||
|
|
7c0c48da01 | ||
|
|
ff5010cdd0 | ||
|
|
0940c922ef | ||
|
|
83ff0eab10 | ||
|
|
c66ff60c58 | ||
|
|
feaf834184 | ||
|
|
805e9288fa | ||
|
|
641069d4fd | ||
|
|
a06fd31412 | ||
|
|
07aa718cb9 | ||
|
|
f186ea12c6 | ||
|
|
dc05134e41 | ||
|
|
f96c3312e2 | ||
|
|
d1579f62a3 | ||
|
|
86fb18395e | ||
|
|
bc28223a77 | ||
|
|
f923211e9f | ||
|
|
6f2ba821a6 | ||
|
|
8839278da7 | ||
|
|
485ba4a3e4 | ||
|
|
bd25040e22 | ||
|
|
c4a1764801 | ||
|
|
3c48ee39d9 | ||
|
|
a787cfa33a | ||
|
|
1bd2f0e98f | ||
|
|
393fae74b3 | ||
|
|
a2f6c16764 | ||
|
|
a70461b726 | ||
|
|
6b8ead6c4b | ||
|
|
8b023f57f9 | ||
|
|
eebf0b0499 | ||
|
|
63a01b1e1f | ||
|
|
913c94dbd1 | ||
|
|
0c414eee80 | ||
|
|
b0ccce397a | ||
|
|
6731c55429 | ||
|
|
fdb618219f | ||
|
|
89f4b87038 | ||
|
|
ea869a3008 | ||
|
|
1412fed265 | ||
|
|
de72542151 | ||
|
|
03d0488335 | ||
|
|
925d5e1dea | ||
|
|
d8b3654011 | ||
|
|
51c7917853 | ||
|
|
d30bf938ee | ||
|
|
5d8f449ea0 | ||
|
|
290f6ed2ad | ||
|
|
98a46c4497 | ||
|
|
d02ffd33b3 | ||
|
|
a5b73a365d | ||
|
|
71df504834 | ||
|
|
8cfbb8d0b3 | ||
|
|
0f4b8be2b0 | ||
|
|
af97a84108 | ||
|
|
e28f402b6b | ||
|
|
0dfbe01c73 | ||
|
|
90900b1a1f | ||
|
|
4295f4ec31 | ||
|
|
c530a061cc | ||
|
|
04ad634986 | ||
|
|
d4b9c3c014 | ||
|
|
bc3515fd1b | ||
|
|
b033ce0669 | ||
|
|
7035a4647d | ||
|
|
39345b0b61 | ||
|
|
8b8e694c60 | ||
|
|
a98f085be6 | ||
|
|
128ff12f8f | ||
|
|
e28652489e | ||
|
|
dae0fafc8a | ||
|
|
19eed51582 | ||
|
|
9ff22f596d | ||
|
|
e1af9f133f | ||
|
|
26e253f4a5 | ||
|
|
2854a2006e | ||
|
|
518824e41b | ||
|
|
3bf1904269 | ||
|
|
2c2a332f80 | ||
|
|
8759b42e6b | ||
|
|
bf937452c6 | ||
|
|
9243430b75 | ||
|
|
b21d5bbf04 | ||
|
|
5119dc3ad5 | ||
|
|
476dd1cef5 | ||
|
|
a8ba3eb7f9 | ||
|
|
d3b270d025 | ||
|
|
b8f3c8a445 | ||
|
|
eb46f403d6 | ||
|
|
9c0ec8cb10 | ||
|
|
d268412738 | ||
|
|
e360aadcd4 | ||
|
|
f88b3cddb6 | ||
|
|
56548fcaf0 | ||
|
|
fce9cc205c | ||
|
|
701b7acd80 | ||
|
|
819181007c | ||
|
|
7cf52044b2 | ||
|
|
d1468eaa45 | ||
|
|
afb4a59b19 | ||
|
|
b1a05dfce2 | ||
|
|
62a1c187a2 | ||
|
|
1db3134df8 | ||
|
|
ffc0d7fc7c | ||
|
|
fe2f65daac | ||
|
|
ef8adc15f4 | ||
|
|
1ac9bd510b | ||
|
|
4d0d28c222 | ||
|
|
6b0c58bab3 | ||
|
|
3716fa44ac | ||
|
|
d3dee07956 | ||
|
|
986540ab34 | ||
|
|
c9aa5e638e | ||
|
|
8d879867b5 | ||
|
|
92027e4c57 | ||
|
|
081ad09ed8 | ||
|
|
b0d88be420 | ||
|
|
57d3c44bb7 | ||
|
|
a70cab502c | ||
|
|
12b2e72162 | ||
|
|
a45a2acc4c | ||
|
|
8cbdbb2fed | ||
|
|
9e2ab2750a | ||
|
|
aabd47ecdf | ||
|
|
248da4687a | ||
|
|
2b44cb70af | ||
|
|
906538e6aa | ||
|
|
75c354fd94 | ||
|
|
9bd5d31c1f | ||
|
|
903a4dd849 | ||
|
|
de6eab6d5d | ||
|
|
8c1a3253fb | ||
|
|
375e01a074 | ||
|
|
74f5c1c160 | ||
|
|
5f889dd3c6 | ||
|
|
50ccb5e5f6 | ||
|
|
9793e6aa25 | ||
|
|
2dd6a2e655 | ||
|
|
1840cc54e4 | ||
|
|
7190820fc4 | ||
|
|
80e9b08bb6 | ||
|
|
4d762159ef | ||
|
|
a7bc294a08 | ||
|
|
8e0cc2a5ea | ||
|
|
bc12193c3c | ||
|
|
7dd7b6b984 | ||
|
|
711aa83d31 | ||
|
|
a6e1413d58 | ||
|
|
a4ec32ba66 | ||
|
|
9a19e95b60 | ||
|
|
f0bbcbf0fd | ||
|
|
223713bc53 | ||
|
|
330f2acc29 | ||
|
|
4010e5e247 | ||
|
|
574fd42534 | ||
|
|
9083e5fe5c | ||
|
|
1eb2d894e5 | ||
|
|
9b75b603e3 | ||
|
|
20d0fa1b4e | ||
|
|
faf830dc77 | ||
|
|
a3a1761eb7 | ||
|
|
6dd8592434 | ||
|
|
aaf25ad4f0 | ||
|
|
73ba06b26b | ||
|
|
92245e3fc8 | ||
|
|
1dd2a90e20 | ||
|
|
25d785df02 | ||
|
|
60d8236f05 | ||
|
|
54ebc21880 | ||
|
|
bd706418c9 | ||
|
|
0371a562d9 | ||
|
|
3248557897 | ||
|
|
00ea849cb9 | ||
|
|
0fbbe52ea7 | ||
|
|
5421c862e0 | ||
|
|
d9ffc4fa21 | ||
|
|
fea4c54adc | ||
|
|
8e5a6f9bf2 | ||
|
|
988d660083 | ||
|
|
4071dd7734 | ||
|
|
8faa6f1f4d | ||
|
|
19f54111ec | ||
|
|
cedc095a9c | ||
|
|
4c2ddfdf54 | ||
|
|
0fb99a0098 | ||
|
|
6d18ba4b32 | ||
|
|
649bca7618 | ||
|
|
d01be82b35 | ||
|
|
3103e0fd0a | ||
|
|
fe76b2428b | ||
|
|
2a818947c2 | ||
|
|
83b4279ce1 | ||
|
|
9c16a58d52 | ||
|
|
cb2bd0fe1a | ||
|
|
a42e0d6a31 | ||
|
|
056ba75a72 | ||
|
|
b6ab2852c2 | ||
|
|
9cbc335b68 | ||
|
|
e2f5ac60cf | ||
|
|
cb43e3d763 | ||
|
|
31dccc9a5e | ||
|
|
c66ce3a209 | ||
|
|
7c99a8b641 | ||
|
|
46d3ed7832 | ||
|
|
d9af93850c | ||
|
|
656fc5d7c0 | ||
|
|
1e57f277b8 | ||
|
|
4700b69158 | ||
|
|
1874820008 | ||
|
|
5b92b2bfb6 | ||
|
|
26f11078dc | ||
|
|
f5ff24ccdb | ||
|
|
a5c1bf1abf | ||
|
|
1ec2647b25 | ||
|
|
06ad4caa1b | ||
|
|
b0464f1751 | ||
|
|
5bd4ea30ad | ||
|
|
d336667972 | ||
|
|
50dd07217d | ||
|
|
ab8f4720d6 | ||
|
|
fe972ce12b | ||
|
|
c6483f8b1e | ||
|
|
12ab553d76 | ||
|
|
c8516c2349 | ||
|
|
6082e49252 | ||
|
|
7774d5c6b8 | ||
|
|
6cbfbeca0b | ||
|
|
00ddf03ad3 | ||
|
|
940715a975 | ||
|
|
a6e852495d | ||
|
|
34451d159b | ||
|
|
089a9e3321 | ||
|
|
c9da02912e | ||
|
|
25dbf1cfac | ||
|
|
945404be24 | ||
|
|
698483ee17 | ||
|
|
a877759d13 | ||
|
|
c4adf6805a | ||
|
|
730a2f7946 | ||
|
|
5cde17b38f | ||
|
|
9131e7e73c | ||
|
|
92941d0838 | ||
|
|
eab047c130 | ||
|
|
bca4c35708 | ||
|
|
9aa4c3cf22 | ||
|
|
b0a1550df5 | ||
|
|
89fade7ae4 | ||
|
|
8c7313f290 | ||
|
|
8e5db87b50 | ||
|
|
98c63bc195 | ||
|
|
6f6a467c7b | ||
|
|
f0f7321c38 | ||
|
|
da146f13c1 | ||
|
|
684c8dd0ce | ||
|
|
c2ffaa0777 | ||
|
|
632414cc92 | ||
|
|
f1593d3fb8 | ||
|
|
f806d4a5d6 | ||
|
|
da72968085 | ||
|
|
faa5ec5125 | ||
|
|
00fb767ad1 | ||
|
|
ef60512e09 | ||
|
|
ca6b82856e | ||
|
|
817d6cc8c7 | ||
|
|
c4cc752e18 | ||
|
|
4a7013d550 | ||
|
|
bb0bccc3c0 | ||
|
|
b17cbadaf3 | ||
|
|
7d641baf4e | ||
|
|
13fe9e7cf8 | ||
|
|
7fc94b8cad | ||
|
|
3b6e1f4e93 | ||
|
|
63673c4328 | ||
|
|
134daeac7f | ||
|
|
4c71e2ec17 | ||
|
|
d3c30c30d4 | ||
|
|
efb43682a9 | ||
|
|
d478cdfda4 | ||
|
|
d6f202e292 | ||
|
|
01187901ea | ||
|
|
f0ce4d4236 | ||
|
|
fd025ae76b | ||
|
|
9034708c99 | ||
|
|
9db6a5e0df | ||
|
|
f4b4c75d15 | ||
|
|
57c633967a | ||
|
|
1599e6db6e | ||
|
|
537081ec9d | ||
|
|
3020a04a8b | ||
|
|
7067ad1b07 | ||
|
|
790e2dc69f | ||
|
|
236b2b9273 | ||
|
|
9d452b4bc3 | ||
|
|
eb4f7f3b21 | ||
|
|
a551195ddf | ||
|
|
e78bf5f876 | ||
|
|
15ddb2009d | ||
|
|
8667f5ec02 | ||
|
|
05b0189773 | ||
|
|
8ace0e68c3 | ||
|
|
79c51a6a80 | ||
|
|
ab58052a4c | ||
|
|
7124d93a24 | ||
|
|
c67a1d0469 | ||
|
|
9d66d55ac1 | ||
|
|
657c68475d | ||
|
|
ad6e45bf22 | ||
|
|
c8a4fc2c57 | ||
|
|
87cad0d84b | ||
|
|
18031e2ed3 | ||
|
|
ca92bcbf7f | ||
|
|
fa35249afd | ||
|
|
e8ef050093 | ||
|
|
6b687c4318 | ||
|
|
b4aae198fc | ||
|
|
2276279434 | ||
|
|
5a22b360f1 | ||
|
|
f927ee7b4e | ||
|
|
21238ec35c | ||
|
|
42bb09fa31 | ||
|
|
34b8c58c0f | ||
|
|
3e6adbc904 | ||
|
|
a4315f4076 | ||
|
|
6ec5264f20 | ||
|
|
a6b9e5604c | ||
|
|
c999a0d8f9 | ||
|
|
affeadf4f3 | ||
|
|
c597123d1a | ||
|
|
ee35673615 | ||
|
|
9098105a64 | ||
|
|
685acb786a | ||
|
|
d18aa44fb4 | ||
|
|
e907da77d7 | ||
|
|
56e0a52197 | ||
|
|
8b2d47dd71 | ||
|
|
503ce3a47f | ||
|
|
867bb20339 | ||
|
|
b7af96aa4d | ||
|
|
550e5f752a | ||
|
|
cdb3c159c5 | ||
|
|
90db6654d3 | ||
|
|
c8c88dd58c | ||
|
|
5b1a761b0f | ||
|
|
7f8b5e032a | ||
|
|
db56d82dbd | ||
|
|
a485d7be53 | ||
|
|
2a1f40d487 | ||
|
|
d636bc2616 | ||
|
|
9673ce5541 | ||
|
|
cea88bd67f | ||
|
|
2cadf0a260 | ||
|
|
9c48da2a78 | ||
|
|
1a74288413 | ||
|
|
feeac52621 | ||
|
|
2637a9ccd4 | ||
|
|
e907a9aa6d | ||
|
|
24e9cbb292 | ||
|
|
458c2f2682 | ||
|
|
bf184e8599 | ||
|
|
cf47d49fc6 | ||
|
|
15949fcb02 | ||
|
|
9032889c15 | ||
|
|
a33007afb6 | ||
|
|
1ed5730769 | ||
|
|
9f660b48c4 | ||
|
|
473aefacec | ||
|
|
330cb022eb | ||
|
|
2c24b39268 | ||
|
|
352bc6b94b | ||
|
|
998262177f | ||
|
|
0a5de1239f | ||
|
|
f10891b662 | ||
|
|
c1664478d9 | ||
|
|
1fb8846a7a | ||
|
|
c8eeeffdeb | ||
|
|
9ed5fc5595 | ||
|
|
aaac85e541 | ||
|
|
37479a9afe | ||
|
|
ff354f6edc | ||
|
|
fe43c26829 | ||
|
|
30d8a7503b | ||
|
|
bb9a1335ec | ||
|
|
fa6d5f0ee2 | ||
|
|
d62164f6f0 | ||
|
|
f1628ab903 | ||
|
|
d2d5329f15 | ||
|
|
8c69306f2b | ||
|
|
e055589448 | ||
|
|
924b4f651a | ||
|
|
e36b94d905 | ||
|
|
7191048f91 | ||
|
|
b6ba6efe48 | ||
|
|
0570e6e218 | ||
|
|
8bfab22417 | ||
|
|
d3dc26181e | ||
|
|
433a8dd2e3 | ||
|
|
801205a5bc | ||
|
|
dcfa4aca8c | ||
|
|
a1247f8f92 | ||
|
|
944332a84a | ||
|
|
463661b051 | ||
|
|
0caf93dfd9 | ||
|
|
4baa838e3d | ||
|
|
e38dfff213 | ||
|
|
8cc1d02d46 | ||
|
|
794c508b10 | ||
|
|
3f8083e34c | ||
|
|
a2ccf4efa3 | ||
|
|
31be564fa3 | ||
|
|
53fe96db78 | ||
|
|
29df73df4c | ||
|
|
a76e4434fa | ||
|
|
e3a61c580e | ||
|
|
9ee8f72be9 | ||
|
|
23560d9e41 | ||
|
|
859cd30c3a | ||
|
|
36c5490250 | ||
|
|
34648c6f0c | ||
|
|
9a7d56097a | ||
|
|
05c8c74c96 | ||
|
|
9b858907ec | ||
|
|
d58c6ad4e0 | ||
|
|
e173bf33ba | ||
|
|
30ea556a7a | ||
|
|
5484e088aa | ||
|
|
83bb2ce06b | ||
|
|
155d7fddd7 | ||
|
|
9d35806538 | ||
|
|
1103b1eb40 | ||
|
|
6696c44dc0 | ||
|
|
7a57723c01 | ||
|
|
43ce466aa6 | ||
|
|
0a82a6b434 | ||
|
|
8bb473147c | ||
|
|
543f61048f | ||
|
|
b7c9c397d6 | ||
|
|
a58aca1a80 | ||
|
|
16305418f0 | ||
|
|
3ff38e22cf | ||
|
|
b4530cfbe9 | ||
|
|
245f6c0c84 | ||
|
|
010dd5709c | ||
|
|
eb5a5023c6 | ||
|
|
da9a88c45c | ||
|
|
8e5752c9db | ||
|
|
44efe24bf0 | ||
|
|
5ec178acfc | ||
|
|
fe5891fbdb | ||
|
|
287a86938e | ||
|
|
d5ae6bf77a | ||
|
|
a6dfb4ccdd | ||
|
|
d3e559453b | ||
|
|
6a28971218 | ||
|
|
8d3384666a | ||
|
|
2ae812420d | ||
|
|
bb10dc8c02 | ||
|
|
ef32585f57 | ||
|
|
20ac70d3fd | ||
|
|
b5ed8961f7 | ||
|
|
5b179420fd | ||
|
|
7f7c0bac1e | ||
|
|
83d3dfbc54 | ||
|
|
a6a6a62dc0 | ||
|
|
d58499acfb | ||
|
|
0c21b8c3a6 | ||
|
|
efd44d86c5 | ||
|
|
ae54dd848d | ||
|
|
5f97f23c12 | ||
|
|
1084170b52 | ||
|
|
089514d8a6 | ||
|
|
6119572934 | ||
|
|
31f82243fc | ||
|
|
672dd295f5 | ||
|
|
f97ca7c531 | ||
|
|
fc777e166c | ||
|
|
a80808c5c9 | ||
|
|
c4bb29b4bb | ||
|
|
1e4958774f | ||
|
|
61ab6d5b8b | ||
|
|
3aae4d22f6 | ||
|
|
6ff118e95c | ||
|
|
28af722a85 | ||
|
|
868e76fcf3 | ||
|
|
1289e84cdb | ||
|
|
16954fc370 | ||
|
|
c8aa708cff | ||
|
|
4df7fd644d | ||
|
|
41cdd9db70 | ||
|
|
fbeef3e75f | ||
|
|
b36f0d89bc | ||
|
|
29386f7976 | ||
|
|
b928de0a10 | ||
|
|
0f7440d344 | ||
|
|
9f6d9f7403 | ||
|
|
cc9fb4c12e | ||
|
|
12db976e53 | ||
|
|
45eac1093d | ||
|
|
3470b1839d | ||
|
|
2876317f08 | ||
|
|
2fa55550f3 | ||
|
|
6bdc49048a | ||
|
|
07044eeeaa | ||
|
|
a72c297d5d | ||
|
|
6d003555ea | ||
|
|
52de3fff06 | ||
|
|
afc972278c | ||
|
|
9ca72dc5c6 | ||
|
|
80177e8712 | ||
|
|
125b178297 | ||
|
|
430fe60533 | ||
|
|
ed5e880931 | ||
|
|
adf5da0b2c | ||
|
|
c9b7504e3f | ||
|
|
d82acacd96 | ||
|
|
251b41a7ac | ||
|
|
3fce14d9ce | ||
|
|
5423e0534b | ||
|
|
2365445a3e | ||
|
|
3b5ca2bac0 | ||
|
|
cf0ee31bc5 | ||
|
|
77cc3db1c5 | ||
|
|
9f761a224d | ||
|
|
82affa3fb7 | ||
|
|
4583a45e78 | ||
|
|
8c93fdfb62 | ||
|
|
56bb37a9cf | ||
|
|
8f542618f7 | ||
|
|
d761085318 | ||
|
|
070bb9bf6a | ||
|
|
d105aa92e2 | ||
|
|
486a0c9528 | ||
|
|
02f043d9a6 | ||
|
|
6b991b850e | ||
|
|
852e9274d4 | ||
|
|
db9b1bcb21 | ||
|
|
a955364f0e | ||
|
|
ca9956c36b | ||
|
|
07e2136b3b | ||
|
|
1b83928652 | ||
|
|
0406d6738f | ||
|
|
121ec2603b | ||
|
|
9fa50bded5 | ||
|
|
96bdd32bd0 | ||
|
|
d8b77895db | ||
|
|
ab05216730 | ||
|
|
36080b9833 | ||
|
|
dd6a7b23c3 | ||
|
|
f8a60d5e27 | ||
|
|
3c537f5f28 | ||
|
|
8d81a4a2e4 | ||
|
|
527c61b287 | ||
|
|
c7046b6325 | ||
|
|
ec89aab8fb | ||
|
|
bed3040f5d | ||
|
|
4b9ea5cbe0 | ||
|
|
f938856230 | ||
|
|
81d0ae1160 | ||
|
|
69fa9de4d8 | ||
|
|
c8068b688d | ||
|
|
f5d7f9b09b | ||
|
|
79a64657f7 | ||
|
|
4e1eab99a2 | ||
|
|
9482a2b997 | ||
|
|
18ddb58f72 | ||
|
|
657b4cbeb2 | ||
|
|
4825a857d9 | ||
|
|
5456514499 | ||
|
|
a192b6f50a | ||
|
|
d52d685161 | ||
|
|
c6f922412f | ||
|
|
9d1ccaeefa | ||
|
|
c5b01ca9cb | ||
|
|
91ba048c2a | ||
|
|
11ff5a05e9 | ||
|
|
c36074c7f3 | ||
|
|
7406a095b9 | ||
|
|
876edc0186 | ||
|
|
27b4f0e25d | ||
|
|
a7940c6ffa | ||
|
|
1e059e3fa3 | ||
|
|
01152144bb | ||
|
|
b10e7b8c31 | ||
|
|
f68754290f | ||
|
|
ddcbee2bff | ||
|
|
badba7adaf | ||
|
|
286fb688eb | ||
|
|
b0a33c9765 | ||
|
|
b86079c589 | ||
|
|
3a13981d3f | ||
|
|
3fca731395 | ||
|
|
d2274fa49f | ||
|
|
7e7d67464e | ||
|
|
936efe872a | ||
|
|
2b427dfcd2 | ||
|
|
2f5d11087e | ||
|
|
7be2c55f61 | ||
|
|
7dbb439cf3 | ||
|
|
dac381058c | ||
|
|
48eaebf1c3 | ||
|
|
5b2e390f57 | ||
|
|
86b915d862 | ||
|
|
50424b2ad3 | ||
|
|
1721ccc85e | ||
|
|
e99b08c6da | ||
|
|
cff8a1ecc6 | ||
|
|
9531b9e4ea | ||
|
|
0aeba74185 | ||
|
|
6865b4bbb1 | ||
|
|
81c0e62d79 | ||
|
|
db269e28d6 | ||
|
|
d84ab4f90b | ||
|
|
65251748c7 | ||
|
|
5fbb362710 | ||
|
|
4b7c61111c | ||
|
|
8cb8678525 | ||
|
|
c95aace6e0 | ||
|
|
eba8dac47d | ||
|
|
e1622006c3 | ||
|
|
9a9f4137e5 | ||
|
|
adffe16cb8 | ||
|
|
1e6f099eb0 | ||
|
|
32b16717a7 | ||
|
|
09ec642543 | ||
|
|
41011a95e1 | ||
|
|
fba264433a | ||
|
|
a228c9b32b | ||
|
|
454c5f4d83 | ||
|
|
1a50e77a4d | ||
|
|
41d8d17062 | ||
|
|
124bf43a27 | ||
|
|
6929dddacf | ||
|
|
0e0140d88b | ||
|
|
f377a45c4e | ||
|
|
c4da7637d6 |
No files matched your search
@@ -49,3 +49,9 @@ target
|
||||
# Maven shade
|
||||
#############
|
||||
*dependency-reduced-pom.xml
|
||||
|
||||
# nodejs #
|
||||
##########
|
||||
# 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
|
||||
+11
-16
@@ -1,33 +1,28 @@
|
||||
language: java
|
||||
dist: precise
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
before_cache:
|
||||
- rm -rf $HOME/.m2/repository/org/keycloak
|
||||
cache: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- MAVEN_SKIP_RC=true
|
||||
- MAVEN_OPTS="-Xms512m -Xmx2048m"
|
||||
- MAVEN_OPTS="-Xms512m -Xmx1536m"
|
||||
matrix:
|
||||
- TESTS=group1
|
||||
- TESTS=group2
|
||||
- TESTS=group3
|
||||
- TESTS=group4
|
||||
- TESTS=unit
|
||||
- TESTS=server-group1
|
||||
- TESTS=server-group2
|
||||
- TESTS=server-group3
|
||||
- TESTS=server-group4
|
||||
- TESTS=old
|
||||
- TESTS=crossdc
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
before_script:
|
||||
- export MAVEN_SKIP_RC=true
|
||||
install: true
|
||||
|
||||
install:
|
||||
- travis_wait 60 mvn install --no-snapshot-updates -Pdistribution -DskipTestsuite -B -V -q
|
||||
|
||||
script:
|
||||
script:
|
||||
- ./travis-run-tests.sh $TESTS
|
||||
|
||||
sudo: false
|
||||
@@ -38,7 +38,7 @@ Starting Keycloak
|
||||
|
||||
To start Keycloak during development first build as specified above, then run:
|
||||
|
||||
mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
|
||||
mvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server
|
||||
|
||||
|
||||
To start Keycloak from the server distribution first build the distribution it as specified above, then run:
|
||||
@@ -49,6 +49,10 @@ To start Keycloak from the server distribution first build the distribution it a
|
||||
|
||||
To stop the server press `Ctrl + C`.
|
||||
|
||||
Reporting security vulnerabilities
|
||||
----------------------------------
|
||||
|
||||
If you've found a security vulnerability, please look at the [instructions on how to properly report it](http://www.keycloak.org/security.html)
|
||||
|
||||
Help and Documentation
|
||||
----------------------
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+10
@@ -277,6 +277,16 @@ public class AdapterDeploymentContext {
|
||||
delegate.setSslRequired(sslRequired);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfidentialPort() {
|
||||
return delegate.getConfidentialPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfidentialPort(int confidentialPort) {
|
||||
delegate.setConfidentialPort(confidentialPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenStore getTokenStore() {
|
||||
return delegate.getTokenStore();
|
||||
|
||||
+6
-5
@@ -71,9 +71,10 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
||||
AccessTokenResponse atr=null;
|
||||
try {
|
||||
String userpw=new String(Base64.decode(tokenString));
|
||||
String[] parts=userpw.split(":");
|
||||
|
||||
atr = getToken(parts[0], parts[1]);
|
||||
int seperatorIndex = userpw.indexOf(":");
|
||||
String user = userpw.substring(0, seperatorIndex);
|
||||
String pw = userpw.substring(seperatorIndex + 1);
|
||||
atr = getToken(user, pw);
|
||||
tokenString = atr.getToken();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to obtain token", e);
|
||||
@@ -82,8 +83,8 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
||||
}
|
||||
|
||||
return authenticateToken(exchange, atr.getToken());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AccessTokenResponse getToken(String username, String password) throws Exception {
|
||||
AccessTokenResponse tokenResponse=null;
|
||||
HttpClient client = deployment.getClient();
|
||||
|
||||
+19
-1
@@ -23,6 +23,8 @@ import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
@@ -83,6 +85,16 @@ public class BearerTokenRequestAuthenticator {
|
||||
}
|
||||
|
||||
protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
|
||||
log.debug("Verifying access_token");
|
||||
if (log.isTraceEnabled()) {
|
||||
try {
|
||||
JWSInput jwsInput = new JWSInput(tokenString);
|
||||
String wireString = jwsInput.getWireString();
|
||||
log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
|
||||
} catch (JWSInputException e) {
|
||||
log.errorf(e, "Failed to parse access_token: %s", tokenString);
|
||||
}
|
||||
}
|
||||
try {
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
} catch (VerificationException e) {
|
||||
@@ -124,6 +136,7 @@ public class BearerTokenRequestAuthenticator {
|
||||
}
|
||||
surrogate = chain[0].getSubjectDN().getName();
|
||||
}
|
||||
log.debug("successful authorized");
|
||||
return AuthOutcome.AUTHENTICATED;
|
||||
}
|
||||
|
||||
@@ -164,7 +177,12 @@ public class BearerTokenRequestAuthenticator {
|
||||
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
|
||||
facade.getRequest().setError(error);
|
||||
facade.getResponse().addHeader("WWW-Authenticate", challenge);
|
||||
facade.getResponse().sendError(401);
|
||||
if(deployment.isDelegateBearerErrorResponseSending()){
|
||||
facade.getResponse().setStatus(401);
|
||||
}
|
||||
else {
|
||||
facade.getResponse().sendError(401);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
+19
-2
@@ -68,6 +68,7 @@ public class KeycloakDeployment {
|
||||
|
||||
protected String scope;
|
||||
protected SslRequired sslRequired = SslRequired.ALL;
|
||||
protected int confidentialPort = -1;
|
||||
protected TokenStore tokenStore = TokenStore.SESSION;
|
||||
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||
protected boolean useResourceRoleMappings;
|
||||
@@ -94,6 +95,8 @@ public class KeycloakDeployment {
|
||||
|
||||
protected Map<String, String> redirectRewriteRules;
|
||||
|
||||
protected boolean delegateBearerErrorResponseSending = false;
|
||||
|
||||
public KeycloakDeployment() {
|
||||
}
|
||||
|
||||
@@ -275,6 +278,14 @@ public class KeycloakDeployment {
|
||||
this.sslRequired = sslRequired;
|
||||
}
|
||||
|
||||
public int getConfidentialPort() {
|
||||
return confidentialPort;
|
||||
}
|
||||
|
||||
public void setConfidentialPort(int confidentialPort) {
|
||||
this.confidentialPort = confidentialPort;
|
||||
}
|
||||
|
||||
public TokenStore getTokenStore() {
|
||||
return tokenStore;
|
||||
}
|
||||
@@ -456,6 +467,12 @@ public class KeycloakDeployment {
|
||||
public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) {
|
||||
this.redirectRewriteRules = redirectRewriteRules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isDelegateBearerErrorResponseSending() {
|
||||
return delegateBearerErrorResponseSending;
|
||||
}
|
||||
|
||||
public void setDelegateBearerErrorResponseSending(boolean delegateBearerErrorResponseSending) {
|
||||
this.delegateBearerErrorResponseSending = delegateBearerErrorResponseSending;
|
||||
}
|
||||
}
|
||||
+5
@@ -78,6 +78,11 @@ public class KeycloakDeploymentBuilder {
|
||||
} else {
|
||||
deployment.setSslRequired(SslRequired.EXTERNAL);
|
||||
}
|
||||
|
||||
if (adapterConfig.getConfidentialPort() != -1) {
|
||||
deployment.setConfidentialPort(adapterConfig.getConfidentialPort());
|
||||
}
|
||||
|
||||
if (adapterConfig.getTokenStore() != null) {
|
||||
deployment.setTokenStore(TokenStore.valueOf(adapterConfig.getTokenStore().toUpperCase()));
|
||||
} else {
|
||||
|
||||
+25
-2
@@ -66,7 +66,7 @@ public class OAuthRequestAuthenticator {
|
||||
this.reqAuthenticator = requestAuthenticator;
|
||||
this.facade = facade;
|
||||
this.deployment = deployment;
|
||||
this.sslRedirectPort = sslRedirectPort;
|
||||
this.sslRedirectPort = deployment.getConfidentialPort() != -1 ? deployment.getConfidentialPort() : sslRedirectPort;
|
||||
this.tokenStore = tokenStore;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,9 @@ public class OAuthRequestAuthenticator {
|
||||
String maxAge = getQueryParamValue(OAuth2Constants.MAX_AGE);
|
||||
url = UriUtils.stripQueryParam(url, OAuth2Constants.MAX_AGE);
|
||||
|
||||
String uiLocales = getQueryParamValue(OAuth2Constants.UI_LOCALES_PARAM);
|
||||
url = UriUtils.stripQueryParam(url, OAuth2Constants.UI_LOCALES_PARAM);
|
||||
|
||||
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
|
||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
||||
@@ -188,6 +191,9 @@ public class OAuthRequestAuthenticator {
|
||||
if (maxAge != null && maxAge.length() > 0) {
|
||||
redirectUriBuilder.queryParam(OAuth2Constants.MAX_AGE, maxAge);
|
||||
}
|
||||
if (uiLocales != null && uiLocales.length() > 0) {
|
||||
redirectUriBuilder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, uiLocales);
|
||||
}
|
||||
|
||||
scope = TokenUtil.attachOIDCScope(scope);
|
||||
redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
|
||||
@@ -331,7 +337,7 @@ public class OAuthRequestAuthenticator {
|
||||
} catch (ServerRequest.HttpFailure failure) {
|
||||
log.error("failed to turn code into token");
|
||||
log.error("status from server: " + failure.getStatus());
|
||||
if (failure.getStatus() == 400 && failure.getError() != null) {
|
||||
if (failure.getError() != null) {
|
||||
log.error(" " + failure.getError());
|
||||
}
|
||||
return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null);
|
||||
@@ -344,6 +350,14 @@ public class OAuthRequestAuthenticator {
|
||||
tokenString = tokenResponse.getToken();
|
||||
refreshToken = tokenResponse.getRefreshToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
|
||||
log.debug("Verifying tokens");
|
||||
if (log.isTraceEnabled()) {
|
||||
logToken("\taccess_token", tokenString);
|
||||
logToken("\tid_token", idTokenString);
|
||||
logToken("\trefresh_token", refreshToken);
|
||||
}
|
||||
|
||||
try {
|
||||
token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
|
||||
if (idTokenString != null) {
|
||||
@@ -398,4 +412,13 @@ public class OAuthRequestAuthenticator {
|
||||
return originalUri;
|
||||
}
|
||||
|
||||
private void logToken(String name, String token) {
|
||||
try {
|
||||
JWSInput jwsInput = new JWSInput(token);
|
||||
String wireString = jwsInput.getWireString();
|
||||
log.tracef("\t%s: %s", name, wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
|
||||
} catch (JWSInputException e) {
|
||||
log.errorf(e, "Failed to parse %s: %s", name, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -155,7 +155,9 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||
this.refreshToken = response.getRefreshToken();
|
||||
}
|
||||
this.tokenString = tokenString;
|
||||
tokenStore.refreshCallback(this);
|
||||
if (tokenStore != null) {
|
||||
tokenStore.refreshCallback(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class ClientIdAndSecretCredentialsProvider implements ClientCredentialsProvider {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ClientCredentialsProviderUtils.class);
|
||||
private static Logger logger = Logger.getLogger(ClientIdAndSecretCredentialsProvider.class);
|
||||
|
||||
public static final String PROVIDER_ID = CredentialRepresentation.SECRET;
|
||||
|
||||
|
||||
+41
-18
@@ -33,6 +33,7 @@ import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
|
||||
@@ -96,9 +97,9 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return createEmptyAuthorizationContext(true);
|
||||
}
|
||||
|
||||
Set<String> requiredScopes = getRequiredScopes(pathConfig, request);
|
||||
MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
|
||||
|
||||
if (isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
|
||||
if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
|
||||
try {
|
||||
return createAuthorizationContext(accessToken, pathConfig);
|
||||
} catch (Exception e) {
|
||||
@@ -108,7 +109,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
|
||||
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
|
||||
|
||||
if (!challenge(pathConfig, requiredScopes, httpFacade)) {
|
||||
if (!challenge(pathConfig, methodConfig, httpFacade)) {
|
||||
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
|
||||
handleAccessDenied(httpFacade);
|
||||
}
|
||||
@@ -118,9 +119,9 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return createEmptyAuthorizationContext(false);
|
||||
}
|
||||
|
||||
protected abstract boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade);
|
||||
protected abstract boolean challenge(PathConfig pathConfig, MethodConfig methodConfig, OIDCHttpFacade facade);
|
||||
|
||||
protected boolean isAuthorized(PathConfig actualPathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
Request request = httpFacade.getRequest();
|
||||
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
|
||||
|
||||
@@ -146,7 +147,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
|
||||
if (hasResourceScopePermission(methodConfig, permission)) {
|
||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
|
||||
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
|
||||
this.paths.remove(actualPathConfig);
|
||||
@@ -155,7 +156,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
|
||||
if (hasResourceScopePermission(methodConfig, permission)) {
|
||||
hasPermission = true;
|
||||
return true;
|
||||
}
|
||||
@@ -166,7 +167,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGGER.debugf("Authorization FAILED for path [%s]. No enough permissions [%s].", actualPathConfig, permissions);
|
||||
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -186,9 +187,29 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
|
||||
private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
|
||||
List<String> requiredScopes = methodConfig.getScopes();
|
||||
Set<String> allowedScopes = permission.getScopes();
|
||||
return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
|
||||
|
||||
if (allowedScopes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PolicyEnforcerConfig.ScopeEnforcementMode enforcementMode = methodConfig.getScopesEnforcementMode();
|
||||
|
||||
if (PolicyEnforcerConfig.ScopeEnforcementMode.ALL.equals(enforcementMode)) {
|
||||
return allowedScopes.containsAll(requiredScopes);
|
||||
}
|
||||
|
||||
if (PolicyEnforcerConfig.ScopeEnforcementMode.ANY.equals(enforcementMode)) {
|
||||
for (String requiredScope : requiredScopes) {
|
||||
if (allowedScopes.contains(requiredScope)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requiredScopes.isEmpty();
|
||||
}
|
||||
|
||||
protected AuthzClient getAuthzClient() {
|
||||
@@ -236,20 +257,22 @@ public abstract class AbstractPolicyEnforcer {
|
||||
return request.getRelativePath();
|
||||
}
|
||||
|
||||
private Set<String> getRequiredScopes(PathConfig pathConfig, Request request) {
|
||||
Set<String> requiredScopes = new HashSet<>();
|
||||
|
||||
requiredScopes.addAll(pathConfig.getScopes());
|
||||
|
||||
private MethodConfig getRequiredScopes(PathConfig pathConfig, Request request) {
|
||||
String method = request.getMethod();
|
||||
|
||||
for (PolicyEnforcerConfig.MethodConfig methodConfig : pathConfig.getMethods()) {
|
||||
for (MethodConfig methodConfig : pathConfig.getMethods()) {
|
||||
if (methodConfig.getMethod().equals(method)) {
|
||||
requiredScopes.addAll(methodConfig.getScopes());
|
||||
return methodConfig;
|
||||
}
|
||||
}
|
||||
|
||||
return requiredScopes;
|
||||
MethodConfig methodConfig = new MethodConfig();
|
||||
|
||||
methodConfig.setMethod(request.getMethod());
|
||||
methodConfig.setScopes(pathConfig.getScopes());
|
||||
methodConfig.setScopesEnforcementMode(PolicyEnforcerConfig.ScopeEnforcementMode.ANY);
|
||||
|
||||
return methodConfig;
|
||||
}
|
||||
|
||||
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
|
||||
|
||||
+8
-7
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package org.keycloak.adapters.authorization;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
@@ -26,6 +26,7 @@ import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.representation.PermissionRequest;
|
||||
import org.keycloak.authorization.client.resource.PermissionResource;
|
||||
import org.keycloak.authorization.client.resource.ProtectionResource;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
|
||||
/**
|
||||
@@ -40,9 +41,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
|
||||
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
challengeUmaAuthentication(pathConfig, requiredScopes, facade);
|
||||
challengeUmaAuthentication(pathConfig, methodConfig, facade);
|
||||
} else {
|
||||
challengeEntitlementAuthentication(facade);
|
||||
}
|
||||
@@ -61,10 +62,10 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
}
|
||||
|
||||
private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
|
||||
private void challengeUmaAuthentication(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
|
||||
HttpFacade.Response response = facade.getResponse();
|
||||
AuthzClient authzClient = getAuthzClient();
|
||||
String ticket = getPermissionTicket(pathConfig, requiredScopes, authzClient);
|
||||
String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
|
||||
String clientId = authzClient.getConfiguration().getResource();
|
||||
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
|
||||
response.setStatus(401);
|
||||
@@ -74,12 +75,12 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
}
|
||||
|
||||
private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) {
|
||||
ProtectionResource protection = authzClient.protection();
|
||||
PermissionResource permission = protection.permission();
|
||||
PermissionRequest permissionRequest = new PermissionRequest();
|
||||
permissionRequest.setResourceSetId(pathConfig.getId());
|
||||
permissionRequest.setScopes(requiredScopes);
|
||||
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
|
||||
return permission.forResource(permissionRequest).getTicket();
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -35,6 +35,7 @@ import org.keycloak.authorization.client.representation.EntitlementResponse;
|
||||
import org.keycloak.authorization.client.representation.PermissionRequest;
|
||||
import org.keycloak.authorization.client.representation.PermissionResponse;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
|
||||
@@ -50,14 +51,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAuthorized(PathConfig pathConfig, Set<String> requiredScopes, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
protected boolean isAuthorized(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
AccessToken original = accessToken;
|
||||
|
||||
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
|
||||
if (super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
accessToken = requestAuthorizationToken(pathConfig, requiredScopes, httpFacade);
|
||||
accessToken = requestAuthorizationToken(pathConfig, methodConfig, httpFacade);
|
||||
|
||||
if (accessToken == null) {
|
||||
return false;
|
||||
@@ -78,11 +79,11 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
|
||||
original.setAuthorization(authorization);
|
||||
|
||||
return super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade);
|
||||
return super.isAuthorized(pathConfig, methodConfig, accessToken, httpFacade);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
|
||||
protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
|
||||
handleAccessDenied(facade);
|
||||
return true;
|
||||
}
|
||||
@@ -100,7 +101,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
}
|
||||
}
|
||||
|
||||
private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {
|
||||
private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
|
||||
try {
|
||||
String accessToken = httpFacade.getSecurityContext().getTokenString();
|
||||
AuthzClient authzClient = getAuthzClient();
|
||||
@@ -111,7 +112,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||
PermissionRequest permissionRequest = new PermissionRequest();
|
||||
|
||||
permissionRequest.setResourceSetId(pathConfig.getId());
|
||||
permissionRequest.setScopes(requiredScopes);
|
||||
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
|
||||
|
||||
PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
|
||||
AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ class PathMatcher {
|
||||
pathString = "/";
|
||||
}
|
||||
|
||||
if (matchingUri.equals(targetUri)) {
|
||||
if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) {
|
||||
cache.put(targetUri, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
+19
-2
@@ -37,8 +37,12 @@ import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.Principal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -53,7 +57,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
|
||||
|
||||
public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file";
|
||||
public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class";
|
||||
|
||||
public static final String PROFILE_RESOURCE = "profile:";
|
||||
protected Subject subject;
|
||||
protected CallbackHandler callbackHandler;
|
||||
protected Auth auth;
|
||||
@@ -84,14 +88,27 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
|
||||
|
||||
protected KeycloakDeployment resolveDeployment(String keycloakConfigFile) {
|
||||
try {
|
||||
InputStream is = FindFile.findFile(keycloakConfigFile);
|
||||
InputStream is = null;
|
||||
if (keycloakConfigFile.startsWith(PROFILE_RESOURCE)) {
|
||||
try {
|
||||
is = new URL(keycloakConfigFile).openStream();
|
||||
} catch (MalformedURLException mfue) {
|
||||
throw new RuntimeException(mfue);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
} else {
|
||||
is = FindFile.findFile(keycloakConfigFile);
|
||||
}
|
||||
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
|
||||
return kd;
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean login() throws LoginException {
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public class JWKPublicKeyLocator implements PublicKeyLocator {
|
||||
|
||||
|
||||
private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) {
|
||||
if (lastRequestTime + publicKeyCacheTtl > currentTime) {
|
||||
if (lastRequestTime + publicKeyCacheTtl > currentTime && kid != null) {
|
||||
return currentKeys.get(kid);
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.keycloak.adapters.jbossweb;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
|
||||
|
||||
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
|
||||
|
||||
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
super(deploymentContext, next, container);
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -17,11 +17,15 @@
|
||||
|
||||
package org.keycloak.adapters.jbossweb;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.deploy.LoginConfig;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.tomcat.AbstractAuthenticatedActionsValve;
|
||||
import org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve;
|
||||
import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
|
||||
|
||||
@@ -56,7 +60,6 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
|
||||
super.start();
|
||||
}
|
||||
|
||||
|
||||
public void logout(Request request) {
|
||||
logoutInternal(request);
|
||||
}
|
||||
@@ -65,4 +68,9 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
|
||||
protected GenericPrincipalFactory createPrincipalFactory() {
|
||||
return new JBossWebPrincipalFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
return new AuthenticatedActionsValve(deploymentContext, next, container);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak AS7 / JBoss EAP 6 Integration</name>
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
CLI Single Sign On
|
||||
===================================
|
||||
|
||||
This java-based utility is meant for providing Keycloak integration to
|
||||
command line applications that are either written in Java or another language. The
|
||||
idea is that the Java app provided by this utility performs a login for a specific
|
||||
client, parses responses, and exports an access token as an environment variable
|
||||
that can be used by the command line utility you are accessing.
|
||||
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
export KC_AUTH_SERVER=http://localhost:8080/auth
|
||||
export KC_REALM=master
|
||||
export KC_CLIENT=cli
|
||||
|
||||
export KC_ACCESS_TOKEN=`java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar login`
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
java -DKEYCLOAK_AUTH_SERVER=$KC_AUTH_SERVER -DKEYCLOAK_REALM=$KC_REALM -DKEYCLOAK_CLIENT=$KC_CLIENT -jar target/keycloak-cli-sso-3.3.0.CR1-SNAPSHOT.jar logout
|
||||
|
||||
unset KC_ACCESS_TOKEN
|
||||
|
||||
|
||||
|
||||
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
<?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>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-cli-sso</artifactId>
|
||||
<name>Keycloak CLI SSO Framework</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-installed-adapter</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>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.keycloak.adapters.KeycloakCliSsoMain</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.keycloak.adapters.installed.KeycloakCliSso;
|
||||
import org.keycloak.adapters.installed.KeycloakInstalled;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakCliSsoMain extends KeycloakCliSso {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new KeycloakCliSsoMain().mainCmd(args);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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.installed;
|
||||
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakCliSso {
|
||||
|
||||
public void mainCmd(String[] args) throws Exception {
|
||||
if (args.length != 1) {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("login")) {
|
||||
login();
|
||||
} else if (args[0].equalsIgnoreCase("login-manual")) {
|
||||
loginManual();
|
||||
} else if (args[0].equalsIgnoreCase("token")) {
|
||||
token();
|
||||
} else if (args[0].equalsIgnoreCase("logout")) {
|
||||
logout();
|
||||
} else if (args[0].equalsIgnoreCase("env")) {
|
||||
System.out.println(System.getenv().toString());
|
||||
} else {
|
||||
printHelp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void printHelp() {
|
||||
System.err.println("Commands:");
|
||||
System.err.println(" login - login with desktop browser if available, otherwise do manual login. Output is access token.");
|
||||
System.err.println(" login-manual - manual login");
|
||||
System.err.println(" token - print access token if logged in");
|
||||
System.err.println(" logout - logout.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public AdapterConfig getConfig() {
|
||||
String url = System.getProperty("KEYCLOAK_AUTH_SERVER");
|
||||
if (url == null) {
|
||||
System.err.println("KEYCLOAK_AUTH_SERVER property not set");
|
||||
System.exit(1);
|
||||
}
|
||||
String realm = System.getProperty("KEYCLOAK_REALM");
|
||||
if (realm == null) {
|
||||
System.err.println("KEYCLOAK_REALM property not set");
|
||||
System.exit(1);
|
||||
|
||||
}
|
||||
String client = System.getProperty("KEYCLOAK_CLIENT");
|
||||
if (client == null) {
|
||||
System.err.println("KEYCLOAK_CLIENT property not set");
|
||||
System.exit(1);
|
||||
}
|
||||
String secret = System.getProperty("KEYCLOAK_CLIENT_SECRET");
|
||||
|
||||
|
||||
|
||||
AdapterConfig config = new AdapterConfig();
|
||||
config.setAuthServerUrl(url);
|
||||
config.setRealm(realm);
|
||||
config.setResource(client);
|
||||
config.setSslRequired("external");
|
||||
if (secret != null) {
|
||||
Map<String, Object> creds = new HashMap<>();
|
||||
creds.put("secret", secret);
|
||||
config.setCredentials(creds);
|
||||
} else {
|
||||
config.setPublicClient(true);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public boolean checkToken() throws Exception {
|
||||
String token = getTokenResponse();
|
||||
if (token == null) return false;
|
||||
|
||||
|
||||
if (token != null) {
|
||||
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||
if (m.find()) {
|
||||
String json = m.group(0);
|
||||
try {
|
||||
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
|
||||
return true;
|
||||
}
|
||||
AdapterConfig config = getConfig();
|
||||
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
|
||||
installed.refreshToken(tokenResponse.getRefreshToken());
|
||||
processResponse(installed);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error processing existing token");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
private String getTokenResponse() throws IOException {
|
||||
String token = null;
|
||||
File tokenFile = getTokenFilePath();
|
||||
if (tokenFile.exists()) {
|
||||
FileInputStream fis = new FileInputStream(tokenFile);
|
||||
byte[] data = new byte[(int) tokenFile.length()];
|
||||
fis.read(data);
|
||||
fis.close();
|
||||
token = new String(data, "UTF-8");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public void token() throws Exception {
|
||||
String token = getTokenResponse();
|
||||
if (token == null) {
|
||||
System.err.println("There is no token for client");
|
||||
System.exit(1);
|
||||
} else {
|
||||
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||
if (m.find()) {
|
||||
String json = m.group(0);
|
||||
try {
|
||||
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||
if (Time.currentTime() < tokenResponse.getExpiresIn()) {
|
||||
System.out.println(tokenResponse.getToken());
|
||||
return;
|
||||
} else {
|
||||
System.err.println("token in response file is expired");
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failure processing token response file");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.err.println("Could not find json within token response file");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void login() throws Exception {
|
||||
if (checkToken()) return;
|
||||
AdapterConfig config = getConfig();
|
||||
KeycloakInstalled installed = new KeycloakInstalled(KeycloakDeploymentBuilder.build(config));
|
||||
installed.login();
|
||||
processResponse(installed);
|
||||
}
|
||||
|
||||
public String getHome() {
|
||||
String home = System.getenv("HOME");
|
||||
if (home == null) {
|
||||
home = System.getProperty("HOME");
|
||||
if (home == null) {
|
||||
home = Paths.get("").toAbsolutePath().normalize().toString();
|
||||
}
|
||||
}
|
||||
return home;
|
||||
}
|
||||
|
||||
public File getTokenDirectory() {
|
||||
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM")).toFile();
|
||||
}
|
||||
|
||||
public File getTokenFilePath() {
|
||||
return Paths.get(getHome(), System.getProperty("basepath", ".keycloak-sso"), System.getProperty("KEYCLOAK_REALM"), System.getProperty("KEYCLOAK_CLIENT") + ".json").toFile();
|
||||
}
|
||||
|
||||
private void processResponse(KeycloakInstalled installed) throws IOException {
|
||||
AccessTokenResponse tokenResponse = installed.getTokenResponse();
|
||||
tokenResponse.setExpiresIn(Time.currentTime() + tokenResponse.getExpiresIn());
|
||||
tokenResponse.setIdToken(null);
|
||||
String output = JsonSerialization.writeValueAsString(tokenResponse);
|
||||
getTokenDirectory().mkdirs();
|
||||
FileOutputStream fos = new FileOutputStream(getTokenFilePath());
|
||||
fos.write(output.getBytes("UTF-8"));
|
||||
fos.flush();
|
||||
fos.close();
|
||||
System.out.println(tokenResponse.getToken());
|
||||
}
|
||||
|
||||
public void loginManual() throws Exception {
|
||||
if (checkToken()) return;
|
||||
AdapterConfig config = getConfig();
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||
KeycloakInstalled installed = new KeycloakInstalled(deployment);
|
||||
installed.loginManual();
|
||||
processResponse(installed);
|
||||
}
|
||||
|
||||
public void logout() throws Exception {
|
||||
String token = getTokenResponse();
|
||||
if (token != null) {
|
||||
Matcher m = Pattern.compile("\\{.*\\}\\z").matcher(token);
|
||||
if (m.find()) {
|
||||
String json = m.group(0);
|
||||
try {
|
||||
AccessTokenResponse tokenResponse = JsonSerialization.readValue(json, AccessTokenResponse.class);
|
||||
if (Time.currentTime() > tokenResponse.getExpiresIn()) {
|
||||
System.err.println("Login is expired");
|
||||
System.exit(1);
|
||||
}
|
||||
AdapterConfig config = getConfig();
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||
ServerRequest.invokeLogout(deployment, tokenResponse.getRefreshToken());
|
||||
for (File fp : getTokenDirectory().listFiles()) fp.delete();
|
||||
System.out.println("logout complete");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failure processing token response file");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.err.println("Could not find json within token response file");
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.err.println("Not logged in");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
-13
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.keycloak.adapters.installed;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
@@ -24,6 +25,7 @@ import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
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;
|
||||
@@ -43,6 +45,7 @@ import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -51,6 +54,11 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class KeycloakInstalled {
|
||||
|
||||
public interface HttpResponseWriter {
|
||||
void success(PrintWriter pw, KeycloakInstalled ki);
|
||||
void failure(PrintWriter pw, KeycloakInstalled ki);
|
||||
}
|
||||
|
||||
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
|
||||
|
||||
private KeycloakDeployment deployment;
|
||||
@@ -59,12 +67,18 @@ public class KeycloakInstalled {
|
||||
LOGGED_MANUAL, LOGGED_DESKTOP
|
||||
}
|
||||
|
||||
private AccessTokenResponse tokenResponse;
|
||||
private String tokenString;
|
||||
private String idTokenString;
|
||||
private IDToken idToken;
|
||||
private AccessToken token;
|
||||
private String refreshToken;
|
||||
private Status status;
|
||||
private Locale locale;
|
||||
private HttpResponseWriter loginResponseWriter;
|
||||
private HttpResponseWriter logoutResponseWriter;
|
||||
|
||||
|
||||
|
||||
public KeycloakInstalled() {
|
||||
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
|
||||
@@ -75,6 +89,92 @@ public class KeycloakInstalled {
|
||||
deployment = KeycloakDeploymentBuilder.build(config);
|
||||
}
|
||||
|
||||
public KeycloakInstalled(KeycloakDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
private static HttpResponseWriter defaultLoginWriter = new HttpResponseWriter() {
|
||||
@Override
|
||||
public void success(PrintWriter pw, KeycloakInstalled ki) {
|
||||
pw.println("HTTP/1.1 200 OK");
|
||||
pw.println("Content-Type: text/html");
|
||||
pw.println();
|
||||
pw.println("<html><h1>Login completed.</h1><div>");
|
||||
pw.println("This browser will remain logged in until you close it, logout, or the session expires.");
|
||||
pw.println("</div></html>");
|
||||
pw.flush();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(PrintWriter pw, KeycloakInstalled ki) {
|
||||
pw.println("HTTP/1.1 200 OK");
|
||||
pw.println("Content-Type: text/html");
|
||||
pw.println();
|
||||
pw.println("<html><h1>Login attempt failed.</h1><div>");
|
||||
pw.println("</div></html>");
|
||||
pw.flush();
|
||||
|
||||
}
|
||||
};
|
||||
private static HttpResponseWriter defaultLogoutWriter = new HttpResponseWriter() {
|
||||
@Override
|
||||
public void success(PrintWriter pw, KeycloakInstalled ki) {
|
||||
pw.println("HTTP/1.1 200 OK");
|
||||
pw.println("Content-Type: text/html");
|
||||
pw.println();
|
||||
pw.println("<html><h1>Logout completed.</h1><div>");
|
||||
pw.println("You may close this browser tab.");
|
||||
pw.println("</div></html>");
|
||||
pw.flush();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(PrintWriter pw, KeycloakInstalled ki) {
|
||||
pw.println("HTTP/1.1 200 OK");
|
||||
pw.println("Content-Type: text/html");
|
||||
pw.println();
|
||||
pw.println("<html><h1>Logout failed.</h1><div>");
|
||||
pw.println("You may close this browser tab.");
|
||||
pw.println("</div></html>");
|
||||
pw.flush();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
public HttpResponseWriter getLoginResponseWriter() {
|
||||
if (loginResponseWriter == null) {
|
||||
return defaultLoginWriter;
|
||||
} else {
|
||||
return loginResponseWriter;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponseWriter getLogoutResponseWriter() {
|
||||
if (logoutResponseWriter == null) {
|
||||
return defaultLogoutWriter;
|
||||
} else {
|
||||
return logoutResponseWriter;
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoginResponseWriter(HttpResponseWriter loginResponseWriter) {
|
||||
this.loginResponseWriter = loginResponseWriter;
|
||||
}
|
||||
|
||||
public void setLogoutResponseWriter(HttpResponseWriter logoutResponseWriter) {
|
||||
this.logoutResponseWriter = logoutResponseWriter;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
|
||||
if (isDesktopSupported()) {
|
||||
loginDesktop();
|
||||
@@ -108,19 +208,22 @@ public class KeycloakInstalled {
|
||||
}
|
||||
|
||||
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
CallbackListener callback = new CallbackListener(getLoginResponseWriter());
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||
String state = UUID.randomUUID().toString();
|
||||
|
||||
String authUrl = deployment.getAuthUrl().clone()
|
||||
KeycloakUriBuilder builder = deployment.getAuthUrl().clone()
|
||||
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
|
||||
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
|
||||
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
|
||||
.queryParam(OAuth2Constants.STATE, state)
|
||||
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
|
||||
.build().toString();
|
||||
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID);
|
||||
if (locale != null) {
|
||||
builder.queryParam(OAuth2Constants.UI_LOCALES_PARAM, locale.getLanguage());
|
||||
}
|
||||
String authUrl = builder.build().toString();
|
||||
|
||||
Desktop.getDesktop().browse(new URI(authUrl));
|
||||
|
||||
@@ -144,7 +247,7 @@ public class KeycloakInstalled {
|
||||
}
|
||||
|
||||
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
CallbackListener callback = new CallbackListener(getLogoutResponseWriter());
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||
@@ -167,9 +270,6 @@ public class KeycloakInstalled {
|
||||
}
|
||||
|
||||
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
|
||||
|
||||
String authUrl = deployment.getAuthUrl().clone()
|
||||
@@ -208,7 +308,14 @@ public class KeycloakInstalled {
|
||||
parseAccessToken(tokenResponse);
|
||||
}
|
||||
|
||||
public void refreshToken(String refreshToken) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||
parseAccessToken(tokenResponse);
|
||||
|
||||
}
|
||||
|
||||
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
|
||||
this.tokenResponse = tokenResponse;
|
||||
tokenString = tokenResponse.getToken();
|
||||
refreshToken = tokenResponse.getRefreshToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
@@ -240,6 +347,10 @@ public class KeycloakInstalled {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public AccessTokenResponse getTokenResponse() {
|
||||
return tokenResponse;
|
||||
}
|
||||
|
||||
public boolean isDesktopSupported() {
|
||||
return Desktop.isDesktopSupported();
|
||||
}
|
||||
@@ -248,6 +359,8 @@ public class KeycloakInstalled {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri, null);
|
||||
parseAccessToken(tokenResponse);
|
||||
@@ -269,6 +382,7 @@ public class KeycloakInstalled {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public class CallbackListener extends Thread {
|
||||
|
||||
private ServerSocket server;
|
||||
@@ -283,14 +397,19 @@ public class KeycloakInstalled {
|
||||
|
||||
private String state;
|
||||
|
||||
public CallbackListener() throws IOException {
|
||||
private Socket socket;
|
||||
|
||||
private HttpResponseWriter writer;
|
||||
|
||||
public CallbackListener(HttpResponseWriter writer) throws IOException {
|
||||
this.writer = writer;
|
||||
server = new ServerSocket(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = server.accept();
|
||||
socket = server.accept();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
String request = br.readLine();
|
||||
@@ -314,10 +433,15 @@ public class KeycloakInstalled {
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
pw.println("Please close window and return to application");
|
||||
pw.flush();
|
||||
OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream());
|
||||
PrintWriter pw = new PrintWriter(out);
|
||||
|
||||
if (error == null) {
|
||||
writer.success(pw, KeycloakInstalled.this);
|
||||
} else {
|
||||
writer.failure(pw, KeycloakInstalled.this);
|
||||
}
|
||||
pw.flush();
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
errorException = e;
|
||||
@@ -328,6 +452,8 @@ public class KeycloakInstalled {
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</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>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -38,6 +38,11 @@
|
||||
<plugin>
|
||||
<groupId>com.samaxes.maven</groupId>
|
||||
<artifactId>minify-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<jsEngine>CLOSURE</jsEngine>
|
||||
<closureLanguageIn>ECMASCRIPT5</closureLanguageIn>
|
||||
<closureCreateSourceMap>true</closureCreateSourceMap>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>min-js</id>
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
interval: 5
|
||||
};
|
||||
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
for (var i = 0; i < scripts.length; i++) {
|
||||
if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
|
||||
kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
|
||||
}
|
||||
}
|
||||
|
||||
kc.init = function (initOptions) {
|
||||
kc.authenticated = false;
|
||||
|
||||
@@ -43,7 +50,7 @@
|
||||
} else if (initOptions && initOptions.adapter === 'default') {
|
||||
adapter = loadAdapter();
|
||||
} else {
|
||||
if (window.Cordova) {
|
||||
if (window.Cordova || window.cordova) {
|
||||
adapter = loadAdapter('cordova');
|
||||
} else {
|
||||
adapter = loadAdapter();
|
||||
@@ -214,7 +221,7 @@
|
||||
var callbackState = {
|
||||
state: state,
|
||||
nonce: nonce,
|
||||
redirectUri: encodeURIComponent(redirectUri),
|
||||
redirectUri: encodeURIComponent(redirectUri)
|
||||
}
|
||||
|
||||
if (options && options.prompt) {
|
||||
@@ -831,7 +838,12 @@
|
||||
}
|
||||
|
||||
var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
|
||||
if (kc.iframeVersion) {
|
||||
src = src + '?version=' + kc.iframeVersion;
|
||||
}
|
||||
|
||||
iframe.setAttribute('src', src );
|
||||
iframe.setAttribute('title', 'keycloak-session-iframe' );
|
||||
iframe.style.display = 'none';
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
@@ -936,7 +948,14 @@
|
||||
|
||||
if (type == 'cordova') {
|
||||
loginIframe.enable = false;
|
||||
|
||||
var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
|
||||
if (window.cordova && window.cordova.InAppBrowser) {
|
||||
// Use inappbrowser for IOS and Android if available
|
||||
return window.cordova.InAppBrowser.open(loginUrl, target, options);
|
||||
} else {
|
||||
return window.open(loginUrl, target, options);
|
||||
}
|
||||
};
|
||||
return {
|
||||
login: function(options) {
|
||||
var promise = createPromise();
|
||||
@@ -947,8 +966,7 @@
|
||||
}
|
||||
|
||||
var loginUrl = kc.createLoginUrl(options);
|
||||
var ref = window.open(loginUrl, '_blank', o);
|
||||
|
||||
var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
|
||||
var completed = false;
|
||||
|
||||
ref.addEventListener('loadstart', function(event) {
|
||||
@@ -981,7 +999,7 @@
|
||||
var promise = createPromise();
|
||||
|
||||
var logoutUrl = kc.createLogoutUrl(options);
|
||||
var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
|
||||
var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
|
||||
|
||||
var error;
|
||||
|
||||
@@ -1014,7 +1032,7 @@
|
||||
|
||||
register : function() {
|
||||
var registerUrl = kc.createRegisterUrl();
|
||||
var ref = window.open(registerUrl, '_blank', 'location=no');
|
||||
var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
|
||||
ref.addEventListener('loadstart', function(event) {
|
||||
if (event.url.indexOf('http://localhost') == 0) {
|
||||
ref.close();
|
||||
@@ -1024,7 +1042,7 @@
|
||||
|
||||
accountManagement : function() {
|
||||
var accountUrl = kc.createAccountUrl();
|
||||
var ref = window.open(accountUrl, '_blank', 'location=no');
|
||||
var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
|
||||
ref.addEventListener('loadstart', function(event) {
|
||||
if (event.url.indexOf('http://localhost') == 0) {
|
||||
ref.close();
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
} else if (!init) {
|
||||
var req = new XMLHttpRequest();
|
||||
|
||||
var url = location.href + "/init";
|
||||
var url = location.href.split("?")[0] + "/init";
|
||||
url += "?client_id=" + encodeURIComponent(clientId);
|
||||
url += "&origin=" + encodeURIComponent(origin);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState === 4) {
|
||||
if (req.status === 204) {
|
||||
if (req.status === 204 || req.status === 1223) {
|
||||
init = {
|
||||
clientId: clientId,
|
||||
origin: origin
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</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>cli-sso</module>
|
||||
<module>jaxrs-oauth-client</module>
|
||||
<module>jetty</module>
|
||||
<module>js</module>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</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>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -31,7 +31,9 @@
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>1.3.0.RELEASE</spring-boot.version>
|
||||
<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>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -98,7 +100,19 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class KeycloakRestTemplateCustomizer implements RestTemplateCustomizer {
|
||||
|
||||
private final KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor;
|
||||
|
||||
public KeycloakRestTemplateCustomizer() {
|
||||
this(new KeycloakSecurityContextClientRequestInterceptor());
|
||||
}
|
||||
|
||||
protected KeycloakRestTemplateCustomizer(
|
||||
KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor
|
||||
) {
|
||||
this.keycloakInterceptor = keycloakInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
restTemplate.getInterceptors().add(keycloakInterceptor);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Interceptor for {@link ClientHttpRequestExecution} objects created for server to server secured
|
||||
* communication using OAuth2 bearer tokens issued by Keycloak.
|
||||
*
|
||||
* @author <a href="mailto:jmcshan1@gmail.com">James McShane</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakSecurityContextClientRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
/**
|
||||
* Returns the {@link KeycloakSecurityContext} from the Spring {@link ServletRequestAttributes}'s {@link Principal}.
|
||||
*
|
||||
* The principal must support retrieval of the KeycloakSecurityContext, so at this point, only {@link KeycloakPrincipal}
|
||||
* values are supported
|
||||
*
|
||||
* @return the current <code>KeycloakSecurityContext</code>
|
||||
*/
|
||||
protected KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
Principal principal = attributes.getRequest().getUserPrincipal();
|
||||
if (principal == null) {
|
||||
throw new IllegalStateException("Cannot set authorization header because there is no authenticated principal");
|
||||
}
|
||||
if (!(principal instanceof KeycloakPrincipal)) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Cannot set authorization header because the principal type %s does not provide the KeycloakSecurityContext",
|
||||
principal.getClass()));
|
||||
}
|
||||
return ((KeycloakPrincipal) principal).getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
|
||||
KeycloakSecurityContext context = this.getKeycloakSecurityContext();
|
||||
httpRequest.getHeaders().set(AUTHORIZATION_HEADER, "Bearer " + context.getTokenString());
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class KeycloakRestTemplateCustomizerTest {
|
||||
|
||||
private KeycloakRestTemplateCustomizer customizer;
|
||||
private KeycloakSecurityContextClientRequestInterceptor interceptor =
|
||||
mock(KeycloakSecurityContextClientRequestInterceptor.class);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
customizer = new KeycloakRestTemplateCustomizer(interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interceptorIsAddedToRequest() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
customizer.customize(restTemplate);
|
||||
assertTrue(restTemplate.getInterceptors().contains(interceptor));
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.springboot.client;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Keycloak spring boot client request factory tests.
|
||||
*/
|
||||
public class KeycloakSecurityContextClientRequestInterceptorTest {
|
||||
|
||||
@Spy
|
||||
private KeycloakSecurityContextClientRequestInterceptor factory;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@Mock
|
||||
private KeycloakSecurityContext keycloakSecurityContext;
|
||||
|
||||
@Mock
|
||||
private KeycloakPrincipal keycloakPrincipal;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(servletRequest));
|
||||
servletRequest.setUserPrincipal(keycloakPrincipal);
|
||||
when(keycloakPrincipal.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetKeycloakSecurityContext() throws Exception {
|
||||
KeycloakSecurityContext context = factory.getKeycloakSecurityContext();
|
||||
assertNotNull(context);
|
||||
assertEquals(keycloakSecurityContext, context);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetKeycloakSecurityContextInvalidPrincipal() throws Exception {
|
||||
servletRequest.setUserPrincipal(new MarkerPrincipal());
|
||||
factory.getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetKeycloakSecurityContextNullAuthentication() throws Exception {
|
||||
servletRequest.setUserPrincipal(null);
|
||||
factory.getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
private static class MarkerPrincipal implements Principal {
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+1
-5
@@ -45,11 +45,7 @@ public class HttpHeaderInspectingApiRequestMatcher implements RequestMatcher {
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
boolean ajax = X_REQUESTED_WITH_HEADER_AJAX_VALUE.equals(request.getHeader(X_REQUESTED_WITH_HEADER));
|
||||
boolean html = request.getHeader(HttpHeaders.ACCEPT) != null && request.getHeader(HttpHeaders.ACCEPT).contains(
|
||||
MediaType.TEXT_HTML_VALUE);
|
||||
|
||||
return ajax || !html;
|
||||
return X_REQUESTED_WITH_HEADER_AJAX_VALUE.equals(request.getHeader(X_REQUESTED_WITH_HEADER));
|
||||
}
|
||||
|
||||
}
|
||||
+15
-17
@@ -17,7 +17,16 @@
|
||||
|
||||
package org.keycloak.adapters.springsecurity.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -26,17 +35,6 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
/**
|
||||
* Provides a Keycloak {@link AuthenticationEntryPoint authentication entry point}. Uses a
|
||||
* {@link RequestMatcher} to determine if the request is an interactive login request or a
|
||||
@@ -62,15 +60,14 @@ public class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoin
|
||||
private final RequestMatcher apiRequestMatcher;
|
||||
private String loginUri = DEFAULT_LOGIN_URI;
|
||||
private String realm = DEFAULT_REALM;
|
||||
|
||||
|
||||
private AdapterDeploymentContext adapterDeploymentContext;
|
||||
|
||||
/**
|
||||
* Creates a new Keycloak authentication entry point.
|
||||
*/
|
||||
public KeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext) {
|
||||
this(DEFAULT_API_REQUEST_MATCHER);
|
||||
this.adapterDeploymentContext = adapterDeploymentContext;
|
||||
this(adapterDeploymentContext, DEFAULT_API_REQUEST_MATCHER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,14 +77,15 @@ public class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPoin
|
||||
* @param apiRequestMatcher the <code>RequestMatcher</code> to use to determine
|
||||
* if the current request is an API request or a browser request (required)
|
||||
*/
|
||||
public KeycloakAuthenticationEntryPoint(RequestMatcher apiRequestMatcher) {
|
||||
public KeycloakAuthenticationEntryPoint(AdapterDeploymentContext adapterDeploymentContext, RequestMatcher apiRequestMatcher) {
|
||||
Assert.notNull(apiRequestMatcher, "apiRequestMatcher required");
|
||||
Assert.notNull(adapterDeploymentContext, "adapterDeploymentContext required");
|
||||
this.adapterDeploymentContext = adapterDeploymentContext;
|
||||
this.apiRequestMatcher = apiRequestMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException
|
||||
{
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
|
||||
HttpFacade facade = new SimpleHttpFacade(request, response);
|
||||
if (apiRequestMatcher.matches(request) || adapterDeploymentContext.resolveDeployment(facade).isBearerOnly()) {
|
||||
commenceUnauthorizedResponse(request, response);
|
||||
|
||||
+12
-5
@@ -17,14 +17,13 @@
|
||||
|
||||
package org.keycloak.adapters.springsecurity.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* To return the forbidden code with the corresponding message.
|
||||
@@ -36,6 +35,14 @@ public class KeycloakAuthenticationFailureHandler implements AuthenticationFailu
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate using the Authorization header");
|
||||
// Check that the response was not committed yet (this may happen when another
|
||||
// part of the Keycloak adapter sends a challenge or a redirect).
|
||||
if (!response.isCommitted()) {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate using the Authorization header");
|
||||
} else {
|
||||
if (200 <= response.getStatus() && response.getStatus() < 300) {
|
||||
throw new RuntimeException("Success response was committed while authentication failed!", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -50,7 +50,7 @@ public class KeycloakAuthenticationProvider implements AuthenticationProvider {
|
||||
for (String role : token.getAccount().getRoles()) {
|
||||
grantedAuthorities.add(new KeycloakRole(role));
|
||||
}
|
||||
return new KeycloakAuthenticationToken(token.getAccount(), mapAuthorities(grantedAuthorities));
|
||||
return new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), mapAuthorities(grantedAuthorities));
|
||||
}
|
||||
|
||||
private Collection<? extends GrantedAuthority> mapAuthorities(
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ public class SpringSecurityRequestAuthenticator extends RequestAuthenticator {
|
||||
|
||||
logger.debug("Completing bearer authentication. Bearer roles: {} ",roles);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account));
|
||||
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account, false));
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -23,9 +23,11 @@ import org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
|
||||
import org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakCsrfRequestMatcher;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter;
|
||||
import org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter;
|
||||
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -41,6 +43,7 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
|
||||
/**
|
||||
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
|
||||
@@ -91,6 +94,11 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
return new KeycloakPreAuthActionsFilter(httpSessionManager());
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected KeycloakAuthenticatedActionsFilter keycloakAuthenticatedActionsFilter() {
|
||||
return new KeycloakAuthenticatedActionsFilter();
|
||||
}
|
||||
|
||||
protected KeycloakCsrfRequestMatcher keycloakCsrfRequestMatcher() {
|
||||
return new KeycloakCsrfRequestMatcher();
|
||||
}
|
||||
@@ -117,6 +125,8 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
.and()
|
||||
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
|
||||
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
|
||||
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
|
||||
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
|
||||
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
|
||||
.and()
|
||||
.logout()
|
||||
@@ -124,4 +134,9 @@ public abstract class KeycloakWebSecurityConfigurerAdapter extends WebSecurityCo
|
||||
.logoutUrl("/sso/logout").permitAll()
|
||||
.logoutSuccessUrl("/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
protected KeycloakSecurityContextRequestFilter keycloakSecurityContextRequestFilter() {
|
||||
return new KeycloakSecurityContextRequestFilter();
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -19,6 +19,9 @@ package org.keycloak.adapters.springsecurity.facade;
|
||||
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.KeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -57,7 +60,8 @@ public class SimpleHttpFacade implements OIDCHttpFacade {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
|
||||
if (context != null && context.getAuthentication() != null) {
|
||||
return (KeycloakSecurityContext) context.getAuthentication().getDetails();
|
||||
KeycloakAuthenticationToken authentication = (KeycloakAuthenticationToken) context.getAuthentication();
|
||||
return authentication.getAccount().getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+12
-33
@@ -24,6 +24,7 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
@@ -36,6 +37,7 @@ import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticatio
|
||||
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.keycloak.adapters.springsecurity.token.SpringSecurityAdapterTokenStoreFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -61,19 +63,19 @@ import org.springframework.util.Assert;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
|
||||
|
||||
public static final String DEFAULT_LOGIN_URL = "/sso/login";
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
public static final String SCHEME_BEARER = "bearer ";
|
||||
public static final String SCHEME_BASIC = "basic ";
|
||||
|
||||
|
||||
/**
|
||||
* Request matcher that matches requests to the {@link KeycloakAuthenticationEntryPoint#DEFAULT_LOGIN_URI default login URI}
|
||||
* and any request with a <code>Authorization</code> header.
|
||||
*/
|
||||
public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
|
||||
new OrRequestMatcher(new AntPathRequestMatcher(DEFAULT_LOGIN_URL), new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER));
|
||||
new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
|
||||
new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER),
|
||||
new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN)
|
||||
);
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class);
|
||||
|
||||
@@ -134,6 +136,10 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
|
||||
HttpFacade facade = new SimpleHttpFacade(request, response);
|
||||
KeycloakDeployment deployment = adapterDeploymentContext.resolveDeployment(facade);
|
||||
|
||||
// using Spring authenticationFailureHandler
|
||||
deployment.setDelegateBearerErrorResponseSending(true);
|
||||
|
||||
AdapterTokenStore tokenStore = adapterTokenStoreFactory.createAdapterTokenStore(deployment, request);
|
||||
RequestAuthenticator authenticator
|
||||
= new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, -1);
|
||||
@@ -177,36 +183,10 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request was made with a bearer token authorization header.
|
||||
*
|
||||
* @param request the current <code>HttpServletRequest</code>
|
||||
*
|
||||
* @return <code>true</code> if the <code>request</code> was made with a bearer token authorization header;
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
protected boolean isBearerTokenRequest(HttpServletRequest request) {
|
||||
String authValue = request.getHeader(AUTHORIZATION_HEADER);
|
||||
return authValue != null && authValue.toLowerCase().startsWith(SCHEME_BEARER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request was made with a Basic authentication authorization header.
|
||||
*
|
||||
* @param request the current <code>HttpServletRequest</code>
|
||||
* @return <code>true</code> if the <code>request</code> was made with a Basic authentication authorization header;
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
protected boolean isBasicAuthRequest(HttpServletRequest request) {
|
||||
String authValue = request.getHeader(AUTHORIZATION_HEADER);
|
||||
return authValue != null && authValue.toLowerCase().startsWith(SCHEME_BASIC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
|
||||
Authentication authResult) throws IOException, ServletException {
|
||||
|
||||
if (!(this.isBearerTokenRequest(request) || this.isBasicAuthRequest(request))) {
|
||||
if (authResult instanceof KeycloakAuthenticationToken && ((KeycloakAuthenticationToken) authResult).isInteractive()) {
|
||||
super.successfulAuthentication(request, response, chain, authResult);
|
||||
return;
|
||||
}
|
||||
@@ -227,7 +207,6 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
|
||||
} finally {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2017 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.filter;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
|
||||
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;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class KeycloakSecurityContextRequestFilter extends GenericFilterBean implements ApplicationContextAware {
|
||||
|
||||
private static final String FILTER_APPLIED = KeycloakSecurityContext.class.getPackage().getName() + ".token-refreshed";
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
private AdapterDeploymentContext deploymentContext;
|
||||
|
||||
@Override
|
||||
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) {
|
||||
RefreshableKeycloakSecurityContext refreshableSecurityContext = (RefreshableKeycloakSecurityContext) keycloakSecurityContext;
|
||||
|
||||
if (refreshableSecurityContext.isActive()) {
|
||||
KeycloakDeployment deployment = resolveDeployment(request, response);
|
||||
|
||||
if (deployment.isAlwaysRefreshToken()) {
|
||||
if (refreshableSecurityContext.refreshExpiredToken(false)) {
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), refreshableSecurityContext);
|
||||
} else {
|
||||
clearAuthenticationContext();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearAuthenticationContext();
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilterBean() throws ServletException {
|
||||
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();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2017 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.filter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Spring RequestMatcher that checks for the presence of a query parameter.
|
||||
*
|
||||
* @author <a href="mailto:glavoie@gmail.com">Gabriel Lavoie</a>
|
||||
*/
|
||||
public class QueryParamPresenceRequestMatcher implements RequestMatcher {
|
||||
private String param;
|
||||
|
||||
public QueryParamPresenceRequestMatcher(String param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest httpServletRequest) {
|
||||
return param != null && httpServletRequest.getParameter(param) != null;
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -38,24 +38,27 @@ import java.util.Collection;
|
||||
public class KeycloakAuthenticationToken extends AbstractAuthenticationToken implements Authentication {
|
||||
|
||||
private Principal principal;
|
||||
private boolean interactive;
|
||||
|
||||
/**
|
||||
* Creates a new, unauthenticated Keycloak security token for the given account.
|
||||
*/
|
||||
public KeycloakAuthenticationToken(KeycloakAccount account) {
|
||||
public KeycloakAuthenticationToken(KeycloakAccount account, boolean interactive) {
|
||||
super(null);
|
||||
Assert.notNull(account, "KeycloakAccount cannot be null");
|
||||
Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null");
|
||||
this.principal = account.getPrincipal();
|
||||
this.setDetails(account);
|
||||
this.interactive = interactive;
|
||||
}
|
||||
|
||||
public KeycloakAuthenticationToken(KeycloakAccount account, Collection<? extends GrantedAuthority> authorities) {
|
||||
public KeycloakAuthenticationToken(KeycloakAccount account, boolean interactive, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
Assert.notNull(account, "KeycloakAccount cannot be null");
|
||||
Assert.notNull(account.getPrincipal(), "KeycloakAccount.getPrincipal() cannot be null");
|
||||
this.principal = account.getPrincipal();
|
||||
this.setDetails(account);
|
||||
this.interactive = interactive;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@@ -72,4 +75,8 @@ public class KeycloakAuthenticationToken extends AbstractAuthenticationToken imp
|
||||
public OidcKeycloakAccount getAccount() {
|
||||
return (OidcKeycloakAccount) this.getDetails();
|
||||
}
|
||||
|
||||
public boolean isInteractive() {
|
||||
return interactive;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -105,7 +105,7 @@ public class SpringSecurityTokenStore implements AdapterTokenStore {
|
||||
}
|
||||
|
||||
logger.debug("Saving account info {}", account);
|
||||
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account));
|
||||
SecurityContextHolder.getContext().setAuthentication(new KeycloakAuthenticationToken(account, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-7
@@ -39,14 +39,9 @@ public class HttpHeaderInspectingApiRequestMatcherTest {
|
||||
request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches() throws Exception {
|
||||
assertTrue(apiRequestMatcher.matches(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesBrowserRequest() throws Exception {
|
||||
request.addHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
request.addHeader(HttpHeaders.ACCEPT, "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
assertFalse(apiRequestMatcher.matches(request));
|
||||
}
|
||||
|
||||
@@ -55,7 +50,7 @@ public class HttpHeaderInspectingApiRequestMatcherTest {
|
||||
request.addHeader(
|
||||
HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER,
|
||||
HttpHeaderInspectingApiRequestMatcher.X_REQUESTED_WITH_HEADER_AJAX_VALUE);
|
||||
|
||||
assertTrue(apiRequestMatcher.matches(request));
|
||||
}
|
||||
|
||||
}
|
||||
+26
-12
@@ -17,24 +17,27 @@
|
||||
|
||||
package org.keycloak.adapters.springsecurity.authentication;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Keycloak authentication entry point tests.
|
||||
@@ -46,13 +49,16 @@ public class KeycloakAuthenticationEntryPointTest {
|
||||
private MockHttpServletResponse response;
|
||||
@Mock
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@Mock
|
||||
private AdapterDeploymentContext adapterDeploymentContext;
|
||||
|
||||
|
||||
@Mock
|
||||
private KeycloakDeployment keycloakDeployment;
|
||||
|
||||
@Mock
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -86,8 +92,8 @@ public class KeycloakAuthenticationEntryPointTest {
|
||||
public void testCommenceWithUnauthorizedWithAccept() throws Exception {
|
||||
request.addHeader(HttpHeaders.ACCEPT, "application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
authenticationEntryPoint.commence(request, response, null);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatus());
|
||||
assertNotNull(response.getHeader(HttpHeaders.WWW_AUTHENTICATE));
|
||||
assertEquals(HttpStatus.FOUND.value(), response.getStatus());
|
||||
assertNull(response.getHeader(HttpHeaders.WWW_AUTHENTICATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,6 +106,14 @@ public class KeycloakAuthenticationEntryPointTest {
|
||||
assertEquals(logoutUri, response.getHeader("Location"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommenceWithCustomRequestMatcher() throws Exception {
|
||||
new KeycloakAuthenticationEntryPoint(adapterDeploymentContext, requestMatcher)
|
||||
.commence(request, response, null);
|
||||
|
||||
verify(requestMatcher).matches(request);
|
||||
}
|
||||
|
||||
private void configureBrowserRequest() {
|
||||
request.addHeader(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
}
|
||||
|
||||
+18
-8
@@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock;
|
||||
public class KeycloakAuthenticationProviderTest {
|
||||
private KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
|
||||
private KeycloakAuthenticationToken token;
|
||||
private KeycloakAuthenticationToken interactiveToken;
|
||||
private Set<String> roles = Sets.newSet("user", "admin");
|
||||
|
||||
@Before
|
||||
@@ -52,18 +53,18 @@ public class KeycloakAuthenticationProviderTest {
|
||||
RefreshableKeycloakSecurityContext securityContext = mock(RefreshableKeycloakSecurityContext.class);
|
||||
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, securityContext);
|
||||
|
||||
token = new KeycloakAuthenticationToken(account);
|
||||
token = new KeycloakAuthenticationToken(account, false);
|
||||
interactiveToken = new KeycloakAuthenticationToken(account, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticate() throws Exception {
|
||||
Authentication result = provider.authenticate(token);
|
||||
assertNotNull(result);
|
||||
assertEquals(roles, AuthorityUtils.authorityListToSet(result.getAuthorities()));
|
||||
assertTrue(result.isAuthenticated());
|
||||
assertNotNull(result.getPrincipal());
|
||||
assertNotNull(result.getCredentials());
|
||||
assertNotNull(result.getDetails());
|
||||
assertAuthenticationResult(provider.authenticate(token));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticateInteractive() throws Exception {
|
||||
assertAuthenticationResult(provider.authenticate(interactiveToken));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,4 +84,13 @@ public class KeycloakAuthenticationProviderTest {
|
||||
assertEquals(Sets.newSet("ROLE_USER", "ROLE_ADMIN"),
|
||||
AuthorityUtils.authorityListToSet(result.getAuthorities()));
|
||||
}
|
||||
|
||||
private void assertAuthenticationResult(Authentication result) {
|
||||
assertNotNull(result);
|
||||
assertEquals(roles, AuthorityUtils.authorityListToSet(result.getAuthorities()));
|
||||
assertTrue(result.isAuthenticated());
|
||||
assertNotNull(result.getPrincipal());
|
||||
assertNotNull(result.getCredentials());
|
||||
assertNotNull(result.getDetails());
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.keycloak.adapters.springsecurity.facade;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.spi.KeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.mockito.internal.util.collections.Sets;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class SimpleHttpFacadeTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
SecurityContext springSecurityContext = SecurityContextHolder.createEmptyContext();
|
||||
SecurityContextHolder.setContext(springSecurityContext);
|
||||
Set<String> roles = Sets.newSet("user");
|
||||
Principal principal = mock(Principal.class);
|
||||
RefreshableKeycloakSecurityContext keycloakSecurityContext = mock(RefreshableKeycloakSecurityContext.class);
|
||||
KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, keycloakSecurityContext);
|
||||
KeycloakAuthenticationToken token = new KeycloakAuthenticationToken(account, false);
|
||||
springSecurityContext.setAuthentication(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRetrieveKeycloakSecurityContext() {
|
||||
SimpleHttpFacade facade = new SimpleHttpFacade(new MockHttpServletRequest(), new MockHttpServletResponse());
|
||||
|
||||
assertNotNull(facade.getSecurityContext());
|
||||
}
|
||||
}
|
||||
+3
-32
@@ -53,7 +53,6 @@ import java.util.UUID;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -124,34 +123,6 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
||||
filter.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBearerTokenRequest() throws Exception {
|
||||
assertFalse(filter.isBearerTokenRequest(request));
|
||||
this.setBearerAuthHeader(request);
|
||||
assertTrue(filter.isBearerTokenRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBearerTokenRequestCaseInsensitive() throws Exception {
|
||||
assertFalse(filter.isBearerTokenRequest(request));
|
||||
this.setAuthorizationHeader(request, "bearer");
|
||||
assertTrue(filter.isBearerTokenRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBasicAuthRequest() throws Exception {
|
||||
assertFalse(filter.isBasicAuthRequest(request));
|
||||
this.setBasicAuthHeader(request);
|
||||
assertTrue(filter.isBasicAuthRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBasicAuthRequestCaseInsensitive() throws Exception {
|
||||
assertFalse(filter.isBasicAuthRequest(request));
|
||||
this.setAuthorizationHeader(request, "basic");
|
||||
assertTrue(filter.isBasicAuthRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttemptAuthenticationExpectRedirect() throws Exception {
|
||||
when(keycloakDeployment.getAuthUrl()).thenReturn(KeycloakUriBuilder.fromUri("http://localhost:8080/auth"));
|
||||
@@ -180,7 +151,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulAuthenticationInteractive() throws Exception {
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, true, authorities);
|
||||
filter.successfulAuthentication(request, response, chain, authentication);
|
||||
|
||||
verify(successHandler).onAuthenticationSuccess(eq(request), eq(response), eq(authentication));
|
||||
@@ -189,7 +160,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulAuthenticationBearer() throws Exception {
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, false, authorities);
|
||||
this.setBearerAuthHeader(request);
|
||||
filter.successfulAuthentication(request, response, chain, authentication);
|
||||
|
||||
@@ -200,7 +171,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
|
||||
|
||||
@Test
|
||||
public void testSuccessfulAuthenticationBasicAuth() throws Exception {
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, authorities);
|
||||
Authentication authentication = new KeycloakAuthenticationToken(keycloakAccount, false, authorities);
|
||||
this.setBasicAuthHeader(request);
|
||||
filter.successfulAuthentication(request, response, chain, authentication);
|
||||
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2017 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.filter;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
public class QueryParamPresenceRequestMatcherTest {
|
||||
private static final String ROOT_CONTEXT_PATH = "";
|
||||
|
||||
private static final String VALID_PARAMETER = "access_token";
|
||||
|
||||
private QueryParamPresenceRequestMatcher matcher = new QueryParamPresenceRequestMatcher(VALID_PARAMETER);
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotMatchWithoutQueryParameter() throws Exception {
|
||||
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.EMPTY_MAP);
|
||||
assertFalse(matcher.matches(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesWithValidParameter() throws Exception {
|
||||
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.singletonMap(VALID_PARAMETER, (Object) "123"));
|
||||
assertTrue(matcher.matches(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotMatchWithInvalidParameter() throws Exception {
|
||||
prepareRequest(HttpMethod.GET, ROOT_CONTEXT_PATH, "some/random/uri", Collections.singletonMap("some_parameter", (Object) "123"));
|
||||
assertFalse(matcher.matches(request));
|
||||
}
|
||||
|
||||
private void prepareRequest(HttpMethod method, String contextPath, String uri, Map<String, Object> params) {
|
||||
request.setMethod(method.name());
|
||||
request.setContextPath(contextPath);
|
||||
request.setRequestURI(contextPath + "/" + uri);
|
||||
request.setParameters(params);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak Tomcat Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+4
-5
@@ -31,7 +31,7 @@ import javax.servlet.ServletException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Pre-installed actions that must be authenticated
|
||||
* Abstract base for pre-installed actions that must be authenticated
|
||||
* <p/>
|
||||
* Actions include:
|
||||
* <p/>
|
||||
@@ -41,18 +41,17 @@ import java.io.IOException;
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AuthenticatedActionsValve extends ValveBase {
|
||||
private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
|
||||
public abstract class AbstractAuthenticatedActionsValve extends ValveBase {
|
||||
private static final Logger log = Logger.getLogger(AbstractAuthenticatedActionsValve.class);
|
||||
protected AdapterDeploymentContext deploymentContext;
|
||||
|
||||
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
public AbstractAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
this.deploymentContext = deploymentContext;
|
||||
if (next == null) throw new RuntimeException("Next valve is null!!!");
|
||||
setNext(next);
|
||||
setContainer(container);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
|
||||
+3
-6
@@ -17,11 +17,7 @@
|
||||
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleEvent;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.Manager;
|
||||
import org.apache.catalina.*;
|
||||
import org.apache.catalina.authenticator.FormAuthenticator;
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
@@ -136,7 +132,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||
}
|
||||
|
||||
context.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
|
||||
AuthenticatedActionsValve actions = new AuthenticatedActionsValve(deploymentContext, getNext(), getContainer());
|
||||
AbstractAuthenticatedActionsValve actions = createAuthenticatedActionsValve(deploymentContext, getNext(), getContainer());
|
||||
setNext(actions);
|
||||
|
||||
nodesRegistrationManagement = new NodesRegistrationManagement();
|
||||
@@ -189,6 +185,7 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
||||
|
||||
protected abstract GenericPrincipalFactory createPrincipalFactory();
|
||||
protected abstract boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException;
|
||||
protected abstract AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container);
|
||||
|
||||
protected boolean authenticateInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
|
||||
CatalinaHttpFacade facade = new OIDCCatalinaHttpFacade(request, response);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
|
||||
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
|
||||
|
||||
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
super(deploymentContext, next, container);
|
||||
}
|
||||
}
|
||||
+7
@@ -17,12 +17,15 @@
|
||||
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.deploy.LoginConfig;
|
||||
import org.apache.catalina.realm.GenericPrincipal;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -51,6 +54,10 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
return new AuthenticatedActionsValve(deploymentContext, next, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws LifecycleException {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
|
||||
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
|
||||
|
||||
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
super(deploymentContext, next, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncSupported() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+8
@@ -17,11 +17,14 @@
|
||||
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.deploy.LoginConfig;
|
||||
import org.apache.catalina.realm.GenericPrincipal;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -69,4 +72,9 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
return new AuthenticatedActionsValve(deploymentContext, next, container);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-tomcat-integration-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
|
||||
public class AuthenticatedActionsValve extends AbstractAuthenticatedActionsValve {
|
||||
|
||||
public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
super(deploymentContext, next, container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncSupported() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+8
@@ -17,11 +17,14 @@
|
||||
|
||||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.authenticator.FormAuthenticator;
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.realm.GenericPrincipal;
|
||||
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.AdapterTokenStore;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
@@ -102,4 +105,9 @@ public class KeycloakAuthenticatorValve extends AbstractKeycloakAuthenticatorVal
|
||||
protected AdapterTokenStore getTokenStore(Request request, HttpFacade facade, KeycloakDeployment resolvedDeployment) {
|
||||
return super.getTokenStore(request, facade, resolvedDeployment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAuthenticatedActionsValve createAuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
|
||||
return new AuthenticatedActionsValve(deploymentContext, next, container);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -40,7 +40,7 @@
|
||||
<dependency>
|
||||
<groupId>org.wildfly.common</groupId>
|
||||
<artifactId>wildfly-common</artifactId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wildfly.security</groupId>
|
||||
|
||||
+20
-1
@@ -50,7 +50,9 @@ import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -66,6 +68,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
||||
private ElytronAccount account;
|
||||
private SecurityIdentity securityIdentity;
|
||||
private boolean restored;
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
|
||||
public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
|
||||
this.request = request;
|
||||
@@ -90,6 +93,11 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
||||
|
||||
void authenticationComplete() {
|
||||
if (securityIdentity != null) {
|
||||
HttpScope requestScope = request.getScope(Scope.EXCHANGE);
|
||||
RefreshableKeycloakSecurityContext keycloakSecurityContext = account.getKeycloakSecurityContext();
|
||||
|
||||
requestScope.setAttachment(KeycloakSecurityContext.class.getName(), keycloakSecurityContext);
|
||||
|
||||
this.request.authenticationComplete(response -> {
|
||||
if (!restored) {
|
||||
responseConsumer.accept(response);
|
||||
@@ -256,6 +264,7 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return new Response() {
|
||||
|
||||
@Override
|
||||
public void setStatus(final int status) {
|
||||
responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
|
||||
@@ -263,7 +272,17 @@ class ElytronHttpFacade implements OIDCHttpFacade {
|
||||
|
||||
@Override
|
||||
public void addHeader(final String name, final String value) {
|
||||
responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
|
||||
headers.put(name, value);
|
||||
responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
|
||||
@Override
|
||||
public void accept(HttpServerResponse response) {
|
||||
String latestValue = headers.get(name);
|
||||
|
||||
if (latestValue.equals(value)) {
|
||||
response.addResponseHeader(name, latestValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ public class ElytronSessionTokenStore implements ElytronTokeStore {
|
||||
@Override
|
||||
public void checkCurrentToken() {
|
||||
HttpScope session = httpFacade.getScope(Scope.SESSION);
|
||||
if (!session.exists()) return;
|
||||
if (session == null || !session.exists()) return;
|
||||
RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) session.getAttachment(KeycloakSecurityContext.class.getName());
|
||||
if (securityContext == null) return;
|
||||
|
||||
|
||||
+24
-19
@@ -42,35 +42,40 @@ import java.io.InputStream;
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class KeycloakConfigurationServletListener implements ServletContextListener {
|
||||
|
||||
static final String ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE = AdapterDeploymentContext.class.getName() + ".elytron";
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
ServletContext servletContext = sce.getServletContext();
|
||||
String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
|
||||
KeycloakConfigResolver configResolver;
|
||||
AdapterDeploymentContext deploymentContext;
|
||||
AdapterDeploymentContext deploymentContext = (AdapterDeploymentContext) servletContext.getAttribute(AdapterDeploymentContext.class.getName());
|
||||
|
||||
if (configResolverClass != null) {
|
||||
try {
|
||||
configResolver = (KeycloakConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance();
|
||||
deploymentContext = new AdapterDeploymentContext(configResolver);
|
||||
} catch (Exception ex) {
|
||||
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
|
||||
}
|
||||
} else {
|
||||
InputStream is = getConfigInputStream(servletContext);
|
||||
|
||||
KeycloakDeployment deployment;
|
||||
|
||||
if (is == null) {
|
||||
deployment = new KeycloakDeployment();
|
||||
if (deploymentContext == null) {
|
||||
if (configResolverClass != null) {
|
||||
try {
|
||||
configResolver = (KeycloakConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance();
|
||||
deploymentContext = new AdapterDeploymentContext(configResolver);
|
||||
} catch (Exception ex) {
|
||||
deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
|
||||
}
|
||||
} else {
|
||||
deployment = KeycloakDeploymentBuilder.build(is);
|
||||
}
|
||||
InputStream is = getConfigInputStream(servletContext);
|
||||
|
||||
deploymentContext = new AdapterDeploymentContext(deployment);
|
||||
KeycloakDeployment deployment;
|
||||
|
||||
if (is == null) {
|
||||
deployment = new KeycloakDeployment();
|
||||
} else {
|
||||
deployment = KeycloakDeploymentBuilder.build(is);
|
||||
}
|
||||
|
||||
deploymentContext = new AdapterDeploymentContext(deployment);
|
||||
}
|
||||
}
|
||||
|
||||
servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
|
||||
servletContext.setAttribute(ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE, deploymentContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-3
@@ -31,7 +31,6 @@ import org.keycloak.adapters.AuthenticatedActionsHandler;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.NodesRegistrationManagement;
|
||||
import org.keycloak.adapters.PreAuthActionsHandler;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.RequestAuthenticator;
|
||||
import org.keycloak.adapters.spi.AuthChallenge;
|
||||
import org.keycloak.adapters.spi.AuthOutcome;
|
||||
@@ -71,7 +70,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
|
||||
AdapterDeploymentContext deploymentContext = getDeploymentContext(request);
|
||||
|
||||
if (deploymentContext == null) {
|
||||
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI());
|
||||
LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI(), getMechanismName());
|
||||
request.noAuthenticationInProgress();
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +126,7 @@ class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticat
|
||||
|
||||
private AdapterDeploymentContext getDeploymentContext(HttpServerRequest request) {
|
||||
if (this.deploymentContext == null) {
|
||||
return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(AdapterDeploymentContext.class.getName());
|
||||
return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(KeycloakConfigurationServletListener.ADAPTER_DEPLOYMENT_CONTEXT_ATTRIBUTE);
|
||||
}
|
||||
|
||||
return this.deploymentContext;
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@
|
||||
package org.keycloak.adapters.elytron;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
@@ -54,7 +55,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
|
||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
|
||||
return SupportLevel.UNSUPPORTED;
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ public class KeycloakSecurityRealm implements SecurityRealm {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
|
||||
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
|
||||
return SupportLevel.UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -30,8 +30,10 @@ import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
|
||||
import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
|
||||
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
|
||||
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
|
||||
import org.wildfly.security.auth.server.SecurityIdentity;
|
||||
import org.wildfly.security.credential.BearerTokenCredential;
|
||||
import org.wildfly.security.evidence.Evidence;
|
||||
import org.wildfly.security.http.HttpAuthenticationException;
|
||||
|
||||
@@ -63,8 +65,9 @@ final class SecurityIdentityUtil {
|
||||
}
|
||||
|
||||
SecurityIdentityCallback securityIdentityCallback = new SecurityIdentityCallback();
|
||||
IdentityCredentialCallback credentialCallback = new IdentityCredentialCallback(new BearerTokenCredential(KeycloakPrincipal.class.cast(principal).getKeycloakSecurityContext().getTokenString()), true);
|
||||
|
||||
callbackHandler.handle(new Callback[]{AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback});
|
||||
callbackHandler.handle(new Callback[]{credentialCallback, AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback});
|
||||
|
||||
SecurityIdentity securityIdentity = securityIdentityCallback.getSecurityIdentity();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<name>Keycloak WildFly Integration</name>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<version>3.2.0.CR1-SNAPSHOT</version>
|
||||
<version>3.4.1.CR1</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -68,6 +68,10 @@
|
||||
<artifactId>wildfly-web-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wildfly.security</groupId>
|
||||
<artifactId>wildfly-elytron</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging-annotations</artifactId>
|
||||
@@ -101,5 +105,9 @@
|
||||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+10
-19
@@ -17,41 +17,32 @@
|
||||
|
||||
package org.keycloak.subsystem.adapter.extension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.as.controller.AbstractAddStepHandler;
|
||||
import org.jboss.as.controller.AttributeDefinition;
|
||||
import org.jboss.as.controller.OperationContext;
|
||||
import org.jboss.as.controller.OperationFailedException;
|
||||
import org.jboss.as.controller.SimpleAttributeDefinition;
|
||||
import org.jboss.as.controller.capability.RuntimeCapability;
|
||||
import org.jboss.dmr.ModelNode;
|
||||
|
||||
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
|
||||
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
|
||||
|
||||
/**
|
||||
* Add a deployment to a realm.
|
||||
*
|
||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||
*/
|
||||
public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
|
||||
abstract class AbstractAdapterConfigurationAddHandler extends AbstractAddStepHandler {
|
||||
|
||||
public static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
|
||||
private final boolean elytronEnabled;
|
||||
|
||||
private SecureDeploymentAddHandler() {}
|
||||
|
||||
@Override
|
||||
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||
// TODO: localize exception. get id number
|
||||
if (!operation.get(OP).asString().equals(ADD)) {
|
||||
throw new OperationFailedException("Unexpected operation for add secure deployment. operation=" + operation.toString());
|
||||
}
|
||||
|
||||
for (AttributeDefinition attr : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
|
||||
attr.validateAndSet(operation, model);
|
||||
}
|
||||
AbstractAdapterConfigurationAddHandler(RuntimeCapability<Void> runtimeCapability, List<SimpleAttributeDefinition> attributes) {
|
||||
super(runtimeCapability, attributes);
|
||||
elytronEnabled = runtimeCapability != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||
KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
|
||||
ckService.addSecureDeployment(operation, context.resolveExpressions(model));
|
||||
ckService.addSecureDeployment(operation, context.resolveExpressions(model), elytronEnabled);
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual 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.subsystem.adapter.extension;
|
||||
|
||||
import org.jboss.as.controller.AttributeDefinition;
|
||||
import org.jboss.as.controller.PathElement;
|
||||
import org.jboss.as.controller.SimpleAttributeDefinition;
|
||||
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
|
||||
import org.jboss.as.controller.SimpleResourceDefinition;
|
||||
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
|
||||
import org.jboss.as.controller.operations.validation.IntRangeValidator;
|
||||
import org.jboss.as.controller.operations.validation.StringLengthValidator;
|
||||
import org.jboss.as.controller.registry.ManagementResourceRegistration;
|
||||
import org.jboss.dmr.ModelNode;
|
||||
import org.jboss.dmr.ModelType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Defines attributes and operations for a secure-deployment.
|
||||
*
|
||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||
*/
|
||||
abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefinition {
|
||||
|
||||
protected static final SimpleAttributeDefinition REALM =
|
||||
new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
|
||||
.setXmlName("realm")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition RESOURCE =
|
||||
new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
|
||||
.setXmlName("resource")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
|
||||
new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
|
||||
.setXmlName("use-resource-role-mappings")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition BEARER_ONLY =
|
||||
new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
|
||||
.setXmlName("bearer-only")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
|
||||
new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
|
||||
.setXmlName("enable-basic-auth")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
|
||||
new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
|
||||
.setXmlName("public-client")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION =
|
||||
new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true)
|
||||
.setXmlName("turn-off-change-session-id-on-login")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE =
|
||||
new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true)
|
||||
.setXmlName("token-minimum-time-to-live")
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.setAllowExpression(true)
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS =
|
||||
new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true)
|
||||
.setXmlName("min-time-between-jwks-requests")
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.setAllowExpression(true)
|
||||
.build();
|
||||
|
||||
static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
|
||||
static {
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
|
||||
DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
|
||||
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);
|
||||
}
|
||||
|
||||
static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList();
|
||||
|
||||
static {
|
||||
ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
|
||||
ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
|
||||
}
|
||||
|
||||
static final Map<String, SimpleAttributeDefinition> XML_ATTRIBUTES = new HashMap<String, SimpleAttributeDefinition>();
|
||||
|
||||
static {
|
||||
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
|
||||
XML_ATTRIBUTES.put(def.getXmlName(), def);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
|
||||
static {
|
||||
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
|
||||
DEFINITION_LOOKUP.put(def.getXmlName(), def);
|
||||
}
|
||||
}
|
||||
|
||||
private final AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler;
|
||||
private final List<SimpleAttributeDefinition> attributes;
|
||||
|
||||
protected AbstractAdapterConfigurationDefinition(String name, List<SimpleAttributeDefinition> attributes, AbstractAdapterConfigurationAddHandler addHandler, AbstractAdapterConfigurationRemoveHandler removeHandler, AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler) {
|
||||
super(PathElement.pathElement(name),
|
||||
KeycloakExtension.getResourceDescriptionResolver(name),
|
||||
addHandler,
|
||||
removeHandler);
|
||||
this.attributes = attributes;
|
||||
this.attrWriteHandler = attrWriteHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
|
||||
super.registerOperations(resourceRegistration);
|
||||
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
|
||||
super.registerAttributes(resourceRegistration);
|
||||
for (AttributeDefinition attrDef : this.attributes) {
|
||||
resourceRegistration.registerReadWriteAttribute(attrDef, null, this.attrWriteHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static SimpleAttributeDefinition lookup(String name) {
|
||||
return DEFINITION_LOOKUP.get(name);
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -27,11 +27,7 @@ import org.jboss.dmr.ModelNode;
|
||||
*
|
||||
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
|
||||
*/
|
||||
public final class SecureDeploymentRemoveHandler extends AbstractRemoveStepHandler {
|
||||
|
||||
public static SecureDeploymentRemoveHandler INSTANCE = new SecureDeploymentRemoveHandler();
|
||||
|
||||
private SecureDeploymentRemoveHandler() {}
|
||||
abstract class AbstractAdapterConfigurationRemoveHandler extends AbstractRemoveStepHandler {
|
||||
|
||||
@Override
|
||||
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
|
||||
Loaded 100 of 2452 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user