Compare commits
1014
Commits
1.5.1.Final
...
1.8.0.CR2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6612bcb30 | ||
|
|
b2f9775da5 | ||
|
|
40bb68e265 | ||
|
|
dacb1ebd50 | ||
|
|
063f2aa486 | ||
|
|
4f9e641fda | ||
|
|
1f9ec6a907 | ||
|
|
2648d0f1c5 | ||
|
|
1dfe480c59 | ||
|
|
6b242b26a9 | ||
|
|
7b77cd7d1f | ||
|
|
8a0c53bf1d | ||
|
|
a262d2263e | ||
|
|
bf548945db | ||
|
|
fd2c3996e2 | ||
|
|
ab81456fb3 | ||
|
|
e9445cfc20 | ||
|
|
75d715d8a7 | ||
|
|
62b90ee4e3 | ||
|
|
2f88868e20 | ||
|
|
b0de77013c | ||
|
|
25cd3e3ba8 | ||
|
|
1b0bf966b6 | ||
|
|
d8e28ad96b | ||
|
|
3336d4d7ac | ||
|
|
ee3a880a55 | ||
|
|
c9f9ee9799 | ||
|
|
0536f2c247 | ||
|
|
6f262423f1 | ||
|
|
3c5e7872a7 | ||
|
|
421dc6043d | ||
|
|
9402f89d8e | ||
|
|
81a0135fa6 | ||
|
|
43dc250bf2 | ||
|
|
bc3222ab67 | ||
|
|
e761d6ce10 | ||
|
|
46991764d4 | ||
|
|
747c0f9869 | ||
|
|
5424cd80a8 | ||
|
|
8d2becf681 | ||
|
|
c3f69fdc54 | ||
|
|
45215f361f | ||
|
|
802488cea0 | ||
|
|
3ac3d79e30 | ||
|
|
fe8755d4a4 | ||
|
|
e2b889d727 | ||
|
|
2ec4cb82cc | ||
|
|
bc14cade62 | ||
|
|
a6c852603e | ||
|
|
f1602b3b8d | ||
|
|
7c8bc5814e | ||
|
|
33f83689d5 | ||
|
|
80e2b8eb39 | ||
|
|
fbff61bfba | ||
|
|
ba1a17e668 | ||
|
|
7bc7510d1b | ||
|
|
dc337d684a | ||
|
|
0f9d50924c | ||
|
|
cf0eb1184a | ||
|
|
ecd31010fe | ||
|
|
0985d3c88a | ||
|
|
ddd99c2411 | ||
|
|
7834637749 | ||
|
|
a90d7e54d4 | ||
|
|
bae2b62200 | ||
|
|
71bcf697cc | ||
|
|
9acb4b86aa | ||
|
|
dee0be4237 | ||
|
|
c4544e8caf | ||
|
|
bca3893070 | ||
|
|
280725a2ae | ||
|
|
2892b093ab | ||
|
|
78fe064cf0 | ||
|
|
30aaea73e4 | ||
|
|
4b1b697aa3 | ||
|
|
112a44676d | ||
|
|
f7282d71e5 | ||
|
|
9840c47970 | ||
|
|
8695e16971 | ||
|
|
65f5ce9f13 | ||
|
|
77962ec896 | ||
|
|
54712e29aa | ||
|
|
71b6ed80ae | ||
|
|
4a4bbf26f4 | ||
|
|
64de96d34b | ||
|
|
084873f7fb | ||
|
|
c684dac625 | ||
|
|
a5c159eeff | ||
|
|
1cbe5c4c80 | ||
|
|
4a472b8272 | ||
|
|
ddf74c0d9c | ||
|
|
b2a3918fc3 | ||
|
|
aca00dd42c | ||
|
|
2a8e868bc1 | ||
|
|
979205c827 | ||
|
|
55e36acfc0 | ||
|
|
4b6825806a | ||
|
|
2d878805c1 | ||
|
|
dd038ddbd5 | ||
|
|
f6a02bd408 | ||
|
|
7ec0dad88f | ||
|
|
1e3ec1983d | ||
|
|
7ec02761ef | ||
|
|
7b30a6c9c4 | ||
|
|
0d56713871 | ||
|
|
658f204d92 | ||
|
|
04401af470 | ||
|
|
39d5a07218 | ||
|
|
a65f619c9a | ||
|
|
01c408ba68 | ||
|
|
63975886cb | ||
|
|
c3f2ae1cd1 | ||
|
|
133e4c59e5 | ||
|
|
d3247bc66a | ||
|
|
b6718b44a1 | ||
|
|
15506e3db3 | ||
|
|
977f4051b3 | ||
|
|
49c15f200e | ||
|
|
3e5874b2a1 | ||
|
|
f7ac5fae2a | ||
|
|
22ba4847f4 | ||
|
|
90822e5b34 | ||
|
|
3f62cee836 | ||
|
|
3bacbdf6ff | ||
|
|
edcc39d906 | ||
|
|
3ec516d14c | ||
|
|
d8d0298498 | ||
|
|
8356409b44 | ||
|
|
57971ce0b2 | ||
|
|
b708cd671e | ||
|
|
1b857387f2 | ||
|
|
cea70337ef | ||
|
|
6d5de7b44e | ||
|
|
10eba78036 | ||
|
|
dd6b05224d | ||
|
|
e7009cac47 | ||
|
|
eece3689f2 | ||
|
|
f48753f6e1 | ||
|
|
392ce0b323 | ||
|
|
2fce7f8498 | ||
|
|
c5f19ff2b3 | ||
|
|
b24dfa0009 | ||
|
|
a9b5d07263 | ||
|
|
eddf3eef17 | ||
|
|
dbac147419 | ||
|
|
6752a4f9b0 | ||
|
|
35804aa0df | ||
|
|
41d22986d5 | ||
|
|
9172b5472e | ||
|
|
b27b0411a6 | ||
|
|
0c293089c3 | ||
|
|
5403296ac6 | ||
|
|
20548b402d | ||
|
|
1747e0981f | ||
|
|
ea6374163d | ||
|
|
4782780857 | ||
|
|
b90409c5e4 | ||
|
|
9a921f66ff | ||
|
|
4a492ce13c | ||
|
|
39904548eb | ||
|
|
18268f686c | ||
|
|
1594e07628 | ||
|
|
606e6fa479 | ||
|
|
1afd3d422c | ||
|
|
80fae7116b | ||
|
|
86a0995f29 | ||
|
|
a7c684b909 | ||
|
|
5caf3e0a2d | ||
|
|
d939b6a431 | ||
|
|
42d17577d0 | ||
|
|
eaf326dd37 | ||
|
|
53a542d1e2 | ||
|
|
c51d5e9820 | ||
|
|
817127a751 | ||
|
|
0e6e3d10d8 | ||
|
|
0197c69ac3 | ||
|
|
83eea5036f | ||
|
|
1b614a379b | ||
|
|
0527d441e3 | ||
|
|
6569b22c79 | ||
|
|
d0ec1c3b02 | ||
|
|
2fe7e2a952 | ||
|
|
0d52e4e6c5 | ||
|
|
358c273d39 | ||
|
|
215d59b1e5 | ||
|
|
2b409b3462 | ||
|
|
b4e53b1677 | ||
|
|
051f175f68 | ||
|
|
fd447702e7 | ||
|
|
ee79f5e69d | ||
|
|
de5baa9df7 | ||
|
|
566a58b5d8 | ||
|
|
f837cbfaa9 | ||
|
|
06a55419f1 | ||
|
|
82a573a536 | ||
|
|
d46bad56ae | ||
|
|
32af1bd3a5 | ||
|
|
38b9d05cd7 | ||
|
|
e26e31d8b4 | ||
|
|
2328e0a6f9 | ||
|
|
96e1813b34 | ||
|
|
97357d020b | ||
|
|
b466cec4e5 | ||
|
|
9228c6a16b | ||
|
|
35f8e3c19f | ||
|
|
9956f425eb | ||
|
|
5464e600ea | ||
|
|
cbdba622a5 | ||
|
|
49e9965125 | ||
|
|
dcaf7ea9d5 | ||
|
|
3f51f620b7 | ||
|
|
06e44b185e | ||
|
|
17a2bd266b | ||
|
|
081db0d353 | ||
|
|
b82346761c | ||
|
|
595e7a92e2 | ||
|
|
fee1dea3e6 | ||
|
|
9a58e1276a | ||
|
|
1d401767d6 | ||
|
|
68e3b6c504 | ||
|
|
813ddf365e | ||
|
|
edf3499662 | ||
|
|
df26caed07 | ||
|
|
9f9067663f | ||
|
|
0f3d8b8513 | ||
|
|
4ca3bf530f | ||
|
|
c8c0a45ce4 | ||
|
|
7313b843b9 | ||
|
|
321cac2ab8 | ||
|
|
162dd4051d | ||
|
|
cd19c1acdf | ||
|
|
44ed912154 | ||
|
|
39856c32d1 | ||
|
|
d3a893b171 | ||
|
|
ba861f227e | ||
|
|
4a57bcc0b4 | ||
|
|
87a8e017fa | ||
|
|
b8fe608aaf | ||
|
|
d3e6b38a8e | ||
|
|
8589327cdd | ||
|
|
1b936a1230 | ||
|
|
45c490b640 | ||
|
|
1dd3fc537b | ||
|
|
73cf2404ff | ||
|
|
c16b6e6a43 | ||
|
|
8d23422605 | ||
|
|
9cd8d1612e | ||
|
|
277faea1d0 | ||
|
|
58c7b6090b | ||
|
|
f45bb62fcc | ||
|
|
34c3ffaae1 | ||
|
|
a218cc3034 | ||
|
|
c62b3822f4 | ||
|
|
b0e9720f88 | ||
|
|
6507f58b49 | ||
|
|
46e2440d30 | ||
|
|
9b51b6e43b | ||
|
|
4772b8401a | ||
|
|
2b25c120d4 | ||
|
|
c19e65f93c | ||
|
|
87994e032d | ||
|
|
ee6753d31b | ||
|
|
09c18306c2 | ||
|
|
6124fd64f3 | ||
|
|
2a03d23098 | ||
|
|
ff806eae08 | ||
|
|
e44d6f9888 | ||
|
|
4e79dde17f | ||
|
|
29e05ecf0d | ||
|
|
a388782f9b | ||
|
|
352ea30d20 | ||
|
|
6e0444e17b | ||
|
|
b3f0dc7399 | ||
|
|
ad34d63cb3 | ||
|
|
703720fa43 | ||
|
|
e01c789a93 | ||
|
|
8064c88e3c | ||
|
|
b06e747d28 | ||
|
|
f9b89df326 | ||
|
|
778a96c9e4 | ||
|
|
f6f6b6a258 | ||
|
|
c4f3bd3dda | ||
|
|
9d5356a001 | ||
|
|
32fb604c88 | ||
|
|
b3ea8f3add | ||
|
|
382161c425 | ||
|
|
ec527da868 | ||
|
|
0038c1ce9a | ||
|
|
d8fb0f1540 | ||
|
|
866c11b25b | ||
|
|
64f5d032ed | ||
|
|
635310123e | ||
|
|
0bf53ad0ab | ||
|
|
bf7863b300 | ||
|
|
07e12f3b5f | ||
|
|
0f3d2bbfb4 | ||
|
|
ed30c4454a | ||
|
|
0d4771a763 | ||
|
|
a8317e0cc2 | ||
|
|
5b61a10b55 | ||
|
|
ec327c99f4 | ||
|
|
0492d84b59 | ||
|
|
6a7a823fb4 | ||
|
|
f394e2552a | ||
|
|
5a686c4260 | ||
|
|
642e93be93 | ||
|
|
2a6851b01f | ||
|
|
2726db0be9 | ||
|
|
61c2ec04a4 | ||
|
|
8d6a1d5a1d | ||
|
|
233b1e2553 | ||
|
|
956ea431c8 | ||
|
|
265b6d5e2a | ||
|
|
b22c28b20e | ||
|
|
243acdd04e | ||
|
|
c49394c308 | ||
|
|
fd5cb84dd1 | ||
|
|
54b5a6c41e | ||
|
|
83fa03e8cc | ||
|
|
6863b711e3 | ||
|
|
2c5510284d | ||
|
|
96cfb895db | ||
|
|
46f947113c | ||
|
|
fa4e32ddcb | ||
|
|
57b60797ce | ||
|
|
38d160fab2 | ||
|
|
ef80b64d1c | ||
|
|
8d2e4c0316 | ||
|
|
ae83ad258b | ||
|
|
5fa59262ce | ||
|
|
373fc23fc0 | ||
|
|
58dc8776d6 | ||
|
|
20212d8d70 | ||
|
|
cb20d360c4 | ||
|
|
8e127bf699 | ||
|
|
4d24a874f9 | ||
|
|
7200996ae0 | ||
|
|
34cc750b13 | ||
|
|
0ad6d708da | ||
|
|
c83e3bd2d1 | ||
|
|
5ea880cfff | ||
|
|
4276328794 | ||
|
|
7c4c77d70e | ||
|
|
632216d53f | ||
|
|
d5105164e9 | ||
|
|
7a9e2db18f | ||
|
|
0d582a1326 | ||
|
|
a99df49bc9 | ||
|
|
b996e88dbd | ||
|
|
426a2b46fc | ||
|
|
4a020d545b | ||
|
|
ee363a4c56 | ||
|
|
bf4d5f4df9 | ||
|
|
30ec60b4e2 | ||
|
|
a744ecba9e | ||
|
|
e764461b1a | ||
|
|
f376facb48 | ||
|
|
31614df7c4 | ||
|
|
335f852600 | ||
|
|
c26aeb654b | ||
|
|
969cc6eb9e | ||
|
|
f5326e1f42 | ||
|
|
e664a4a6b2 | ||
|
|
c15571f65c | ||
|
|
1eacdc6904 | ||
|
|
be040eaa18 | ||
|
|
889679c715 | ||
|
|
8ae3551c4d | ||
|
|
cfd4de033b | ||
|
|
d6e2bccb16 | ||
|
|
eeec89583e | ||
|
|
d2bdb74fef | ||
|
|
b04b124351 | ||
|
|
067cb120f8 | ||
|
|
0bab4e63fa | ||
|
|
b48f32380c | ||
|
|
0c8f3f734d | ||
|
|
4f401c7547 | ||
|
|
df74338cbc | ||
|
|
e3060e5e58 | ||
|
|
15309a94cc | ||
|
|
2e39221b98 | ||
|
|
c05e21c049 | ||
|
|
62d519ed37 | ||
|
|
18fa03bf97 | ||
|
|
0bdb05e152 | ||
|
|
09af45201b | ||
|
|
dd85c125cb | ||
|
|
274838aa64 | ||
|
|
ecb39cda0c | ||
|
|
3685a185d4 | ||
|
|
030a35476a | ||
|
|
4ffe86583b | ||
|
|
24f765ceef | ||
|
|
c729835c3e | ||
|
|
507fa4c250 | ||
|
|
bba781beee | ||
|
|
980a1eca5e | ||
|
|
ff63c5552a | ||
|
|
985661c1b9 | ||
|
|
b168656eab | ||
|
|
94ba87ac05 | ||
|
|
cfc28b861b | ||
|
|
38408658f9 | ||
|
|
998bb9986f | ||
|
|
8d2a95d66b | ||
|
|
500bced6ee | ||
|
|
81960a6996 | ||
|
|
ccd456dba6 | ||
|
|
bac39bc71e | ||
|
|
a8c2e79565 | ||
|
|
d6089597ff | ||
|
|
c901fc7480 | ||
|
|
4f2b97de7f | ||
|
|
9a3e023c43 | ||
|
|
142ef50728 | ||
|
|
d2ba4669ab | ||
|
|
ba0b59bfd0 | ||
|
|
f1c3295cec | ||
|
|
04ddf7ba4d | ||
|
|
8f346a5f59 | ||
|
|
22e3abdaa8 | ||
|
|
f1af3e9fb7 | ||
|
|
5e479ba4df | ||
|
|
f6743eff69 | ||
|
|
5a761f2f3a | ||
|
|
261d32f8e7 | ||
|
|
282e702ef6 | ||
|
|
cc014fe0e5 | ||
|
|
95f62901c2 | ||
|
|
cf8b0abaad | ||
|
|
e761fb24e6 | ||
|
|
b1deed67df | ||
|
|
d3cadc9a47 | ||
|
|
32e069eadc | ||
|
|
17c97c2f68 | ||
|
|
48bb4d86e0 | ||
|
|
eda018ba0a | ||
|
|
ac1baa059f | ||
|
|
670b620da7 | ||
|
|
1545593cde | ||
|
|
aedd23a43d | ||
|
|
7e8c80c0df | ||
|
|
9f6813fcad | ||
|
|
14292843ff | ||
|
|
71e25bcd65 | ||
|
|
9f1fa5ebb9 | ||
|
|
98958a2bc4 | ||
|
|
7da9f8085d | ||
|
|
6989589e72 | ||
|
|
4f00f6cceb | ||
|
|
95e729b0b7 | ||
|
|
38c460de82 | ||
|
|
41331111da | ||
|
|
bff334d365 | ||
|
|
41c9289f14 | ||
|
|
1d06d9dd64 | ||
|
|
ec9b2933cc | ||
|
|
1ff5d6eb06 | ||
|
|
bc22abf8cb | ||
|
|
81eb1e1e30 | ||
|
|
e8cb3a416d | ||
|
|
764c20d748 | ||
|
|
c81d0c0898 | ||
|
|
67fca8f1f3 | ||
|
|
22c5c803ca | ||
|
|
f6d716a386 | ||
|
|
62c5bc0e91 | ||
|
|
5e329d7b29 | ||
|
|
1bd9b18803 | ||
|
|
96fe4269ec | ||
|
|
83ff02ea53 | ||
|
|
1df741a307 | ||
|
|
409356f734 | ||
|
|
bad0a95123 | ||
|
|
1bc08fff16 | ||
|
|
ab436705c8 | ||
|
|
ea7709a104 | ||
|
|
1d5a01577e | ||
|
|
4288260aa6 | ||
|
|
600d1f8a56 | ||
|
|
f7b1af7e11 | ||
|
|
4b0e33e388 | ||
|
|
8caaabb2e5 | ||
|
|
1bd0e23f31 | ||
|
|
13a52c1bb2 | ||
|
|
fa7a7d35a9 | ||
|
|
fe5809db4d | ||
|
|
1510d9672a | ||
|
|
9b93468131 | ||
|
|
2e16a55815 | ||
|
|
f65665b5eb | ||
|
|
ce4a865579 | ||
|
|
f2e4e67ba0 | ||
|
|
37f1eaae0e | ||
|
|
6c3a3517b8 | ||
|
|
c4416a25e0 | ||
|
|
550f773293 | ||
|
|
d7ea66ad44 | ||
|
|
2022a8d36b | ||
|
|
0d20e3c7ff | ||
|
|
21119604c6 | ||
|
|
973609d39b | ||
|
|
77bccda7a4 | ||
|
|
0c9f0ea3e6 | ||
|
|
73197332c4 | ||
|
|
ee0dd7e200 | ||
|
|
9203971809 | ||
|
|
4e7fe1de8d | ||
|
|
4646a4aceb | ||
|
|
64baa28301 | ||
|
|
db84571546 | ||
|
|
99cddcef6c | ||
|
|
1891019067 | ||
|
|
b0b4d26fa3 | ||
|
|
6cc76b2e3d | ||
|
|
33ac048c8c | ||
|
|
e25157655b | ||
|
|
213abb87ba | ||
|
|
825a68c6c9 | ||
|
|
816ed9419b | ||
|
|
15f1a38b51 | ||
|
|
1f75f85a20 | ||
|
|
dfb39d13a2 | ||
|
|
b8391e0935 | ||
|
|
ab56460325 | ||
|
|
059ebacffc | ||
|
|
e1a07bd5a8 | ||
|
|
37d7182fc9 | ||
|
|
e1ba26bcae | ||
|
|
b39146f6f5 | ||
|
|
caea1a3458 | ||
|
|
7ef747e7c4 | ||
|
|
4ca442d1b2 | ||
|
|
99e75c69a0 | ||
|
|
1437f7da35 | ||
|
|
fa9abbf016 | ||
|
|
6bce32ba62 | ||
|
|
ee801c2c32 | ||
|
|
2b23497f92 | ||
|
|
04ba5c235e | ||
|
|
1521f56225 | ||
|
|
251adbba0b | ||
|
|
adbf2b22ad | ||
|
|
2b29c3acf4 | ||
|
|
076690e21e | ||
|
|
b586f93182 | ||
|
|
5a318df041 | ||
|
|
cd01ea49b4 | ||
|
|
dd2824ab20 | ||
|
|
8db6972441 | ||
|
|
027311c0c0 | ||
|
|
68edf9ce48 | ||
|
|
7b7fbd3257 | ||
|
|
d0c50fe56d | ||
|
|
4075d65241 | ||
|
|
267be83863 | ||
|
|
15828e899b | ||
|
|
ce089019e6 | ||
|
|
c9c49a9c64 | ||
|
|
623f39862d | ||
|
|
818c3ab1c5 | ||
|
|
1ce028ead7 | ||
|
|
d844e6b4af | ||
|
|
7b4dcb6965 | ||
|
|
7bd0b7ddfc | ||
|
|
d2912673b0 | ||
|
|
28ee95138a | ||
|
|
d46b4eb5fe | ||
|
|
151c56a304 | ||
|
|
c2ad918975 | ||
|
|
634eadb634 | ||
|
|
d26cfcda42 | ||
|
|
4a9a39c08d | ||
|
|
9796b7f06e | ||
|
|
c2e14cb924 | ||
|
|
d896800ec6 | ||
|
|
b74e0dad10 | ||
|
|
2c8b0fc3e1 | ||
|
|
cd640339fc | ||
|
|
52c02f03ae | ||
|
|
84da341912 | ||
|
|
2c3958ce27 | ||
|
|
515f049ce6 | ||
|
|
03acad9b9d | ||
|
|
0392ebab32 | ||
|
|
52804da497 | ||
|
|
31df2046f0 | ||
|
|
f14681f122 | ||
|
|
d7d52b3295 | ||
|
|
8e71910e00 | ||
|
|
e6620ca005 | ||
|
|
490b8ce86f | ||
|
|
480444ffc5 | ||
|
|
2fd7e5b355 | ||
|
|
f45e0aa387 | ||
|
|
6c77c295d5 | ||
|
|
ccfaf72b09 | ||
|
|
700740a378 | ||
|
|
8062f5f50e | ||
|
|
5e0851c25f | ||
|
|
81650a89f9 | ||
|
|
709449243c | ||
|
|
df24d8a01f | ||
|
|
f8f4de9389 | ||
|
|
9755d79879 | ||
|
|
70cbeaf336 | ||
|
|
b0a71fbeb0 | ||
|
|
44f56e1d74 | ||
|
|
74924f2f8c | ||
|
|
c498b06f68 | ||
|
|
525bee66a4 | ||
|
|
cd51a95764 | ||
|
|
57c282eca8 | ||
|
|
ebd02a1a7f | ||
|
|
cc4583bc0b | ||
|
|
11ef67722b | ||
|
|
5316dd6d50 | ||
|
|
3cee33847f | ||
|
|
a2b5b6dc71 | ||
|
|
a5dc91f9a6 | ||
|
|
3b4b1b20d8 | ||
|
|
b32423d96b | ||
|
|
4f2fe0197d | ||
|
|
aa25c899ee | ||
|
|
fa6b99eaab | ||
|
|
e04dbffb88 | ||
|
|
f823b17b90 | ||
|
|
7dfb7ddccd | ||
|
|
4b444d36b0 | ||
|
|
23546402fa | ||
|
|
edfc7ea103 | ||
|
|
3a7c84c9e1 | ||
|
|
6b1287a553 | ||
|
|
c860c671d1 | ||
|
|
072950e9f3 | ||
|
|
8226abef40 | ||
|
|
0d05d38eb6 | ||
|
|
4f4368b29f | ||
|
|
3f8312427a | ||
|
|
73bb579dc5 | ||
|
|
c75528f81e | ||
|
|
28bfac7214 | ||
|
|
1c9a347391 | ||
|
|
df03f51563 | ||
|
|
7d590761dd | ||
|
|
944c416b45 | ||
|
|
028cbe1ce2 | ||
|
|
256ca2b665 | ||
|
|
e4412523d4 | ||
|
|
073d82b367 | ||
|
|
484c5ebb15 | ||
|
|
4c33704c56 | ||
|
|
511763d4bc | ||
|
|
605c80a68d | ||
|
|
2325143803 | ||
|
|
9cc5ca3ce4 | ||
|
|
d13c120b6c | ||
|
|
186de7235c | ||
|
|
9433ef284a | ||
|
|
9942c7b519 | ||
|
|
4587fd23b6 | ||
|
|
32647d8682 | ||
|
|
d8ebf1e3d6 | ||
|
|
c21b56d596 | ||
|
|
4ee5c32b1e | ||
|
|
6386d95904 | ||
|
|
f99395aebe | ||
|
|
82a7173dba | ||
|
|
2bf6f4b0fd | ||
|
|
d8e90028af | ||
|
|
d2afb4892a | ||
|
|
eb9f69eee0 | ||
|
|
39f985e04a | ||
|
|
41a51d924b | ||
|
|
ce996f6a41 | ||
|
|
244848afc4 | ||
|
|
2910db5595 | ||
|
|
a6556a49c2 | ||
|
|
e4961056db | ||
|
|
c8eee38471 | ||
|
|
1e4b3fc3b6 | ||
|
|
14426f26f2 | ||
|
|
19ad846306 | ||
|
|
235ffb2ff6 | ||
|
|
181fdeb0d0 | ||
|
|
8674578d0d | ||
|
|
9cf4186eb2 | ||
|
|
92ed01a61b | ||
|
|
99028e9cab | ||
|
|
20f18eec15 | ||
|
|
67435791ed | ||
|
|
b4520baee5 | ||
|
|
802a39b1ce | ||
|
|
229b964867 | ||
|
|
b413bcb41d | ||
|
|
0c39e30241 | ||
|
|
11a8d3842e | ||
|
|
2c460c3adc | ||
|
|
aaddaa6c25 | ||
|
|
96099e0798 | ||
|
|
e582de2837 | ||
|
|
e1fc863421 | ||
|
|
7de4051c9b | ||
|
|
5b7b5b1b60 | ||
|
|
f8effaee58 | ||
|
|
bf7d43fffb | ||
|
|
870702fd81 | ||
|
|
2a3e5fa7a4 | ||
|
|
90063cafec | ||
|
|
2b6fcc05e1 | ||
|
|
f585ed5582 | ||
|
|
192387047f | ||
|
|
78664e2571 | ||
|
|
bc71739fb2 | ||
|
|
5563118d79 | ||
|
|
aadd63e2b0 | ||
|
|
59d548228a | ||
|
|
68c3f2f65b | ||
|
|
b54bb25121 | ||
|
|
7f65a2be16 | ||
|
|
b907f8f92c | ||
|
|
3381ed50d1 | ||
|
|
c2c2b835b2 | ||
|
|
f92fe6bea9 | ||
|
|
6aa029784a | ||
|
|
325072c74d | ||
|
|
1e48143db0 | ||
|
|
8c72ef2980 | ||
|
|
e7febbd5a5 | ||
|
|
d9211d6bc9 | ||
|
|
8121a9391f | ||
|
|
65b05fd4e4 | ||
|
|
940c9f8bf1 | ||
|
|
c85e0248dd | ||
|
|
2ec143a3ba | ||
|
|
a279ff3d7f | ||
|
|
128c26043b | ||
|
|
fe9b5a6588 | ||
|
|
0f93e72b02 | ||
|
|
bdba5ff8b1 | ||
|
|
7071161a7c | ||
|
|
672103e363 | ||
|
|
f579d7ffe9 | ||
|
|
d1f0b353e8 | ||
|
|
ef56dca050 | ||
|
|
07c3772b08 | ||
|
|
85a886da18 | ||
|
|
4ce93171ba | ||
|
|
c987d57b9e | ||
|
|
a416327124 | ||
|
|
285ef50341 | ||
|
|
0d22074826 | ||
|
|
b76fb68ff7 | ||
|
|
f03128f5ed | ||
|
|
8fb05bacd1 | ||
|
|
f8842b3df1 | ||
|
|
b376d1fb6d | ||
|
|
d4e0901599 | ||
|
|
217c359530 | ||
|
|
4fbf6841be | ||
|
|
2deb20a2c9 | ||
|
|
20b92c410f | ||
|
|
1d829a8365 | ||
|
|
10b4bd24a4 | ||
|
|
788501f13d | ||
|
|
345bea50b6 | ||
|
|
ab7bfa6d90 | ||
|
|
4f7979d9fc | ||
|
|
286458b94a | ||
|
|
61a8c422df | ||
|
|
baaf445727 | ||
|
|
2faf0eccdb | ||
|
|
639b5b617b | ||
|
|
115663a32d | ||
|
|
b277afa6d9 | ||
|
|
960cd8c822 | ||
|
|
4acc69c604 | ||
|
|
0230e008c5 | ||
|
|
382b8d60d0 | ||
|
|
0ad29c9737 | ||
|
|
9ced56d8d7 | ||
|
|
eb197c46f7 | ||
|
|
63947cd37a | ||
|
|
938ce9612c | ||
|
|
d6119c04d6 | ||
|
|
14646781ad | ||
|
|
1b41832946 | ||
|
|
57d0bac96a | ||
|
|
2ca3ce2dba | ||
|
|
e5d404fdb3 | ||
|
|
8829fe8658 | ||
|
|
9ccb0b1f65 | ||
|
|
7bb70f2d00 | ||
|
|
3ab7edbbcd | ||
|
|
d4060cce92 | ||
|
|
1342904fae | ||
|
|
fc0a3c3e73 | ||
|
|
756a9f095c | ||
|
|
021e48d6d6 | ||
|
|
91f78c61b7 | ||
|
|
d39aee0a72 | ||
|
|
7c600e2f4b | ||
|
|
14079b950a | ||
|
|
173c112f8e | ||
|
|
67d8ca0207 | ||
|
|
e690f4e645 | ||
|
|
4c554b4af6 | ||
|
|
3aee5eeecf | ||
|
|
366a1629e5 | ||
|
|
7fec1677ee | ||
|
|
8e391ffa4e | ||
|
|
d65ce5d1d4 | ||
|
|
6741dffe38 | ||
|
|
894af03323 | ||
|
|
80dea9c0bd | ||
|
|
0b83325470 | ||
|
|
368ea64b51 | ||
|
|
450e031da9 | ||
|
|
1796a45a68 | ||
|
|
c9437595b7 | ||
|
|
a1b315adbd | ||
|
|
1781588597 | ||
|
|
f29aff4bed | ||
|
|
ac91839e69 | ||
|
|
9c0c8e37b6 | ||
|
|
d40237b08d | ||
|
|
59527e12f0 | ||
|
|
6d0483667f | ||
|
|
dcf8747d7e | ||
|
|
7a3b4823b0 | ||
|
|
64ecc3b153 | ||
|
|
cc41a1fd78 | ||
|
|
255b1cb3bf | ||
|
|
a1bb202bd4 | ||
|
|
332e3f6099 | ||
|
|
6b41d79058 | ||
|
|
3b4b5c11e6 | ||
|
|
0c9e764e11 | ||
|
|
00cbc22d8b | ||
|
|
0a3de09b5a | ||
|
|
4e0ad5ac0b | ||
|
|
72256fd6ac | ||
|
|
d06237a3fd | ||
|
|
28fad68486 | ||
|
|
53b86cbace | ||
|
|
a53aebcddf | ||
|
|
af307c695a | ||
|
|
7816f053a6 | ||
|
|
ed54ba9064 | ||
|
|
8acdaf462b | ||
|
|
c5efd34a46 | ||
|
|
75343986b0 | ||
|
|
424d357c9c | ||
|
|
56116038d6 | ||
|
|
c76da8f3b8 | ||
|
|
589eb3206e | ||
|
|
5419ec32e4 | ||
|
|
c9e0a7a97d | ||
|
|
5b5f99fdfe | ||
|
|
d3bd0a67a7 | ||
|
|
2a36185419 | ||
|
|
76e3c6394c | ||
|
|
9e15b20591 | ||
|
|
05bd51ac1c | ||
|
|
d648ba73de | ||
|
|
c6b34f32a1 | ||
|
|
974c0a50e7 | ||
|
|
07d3b26722 | ||
|
|
510b03476f | ||
|
|
4f6d3c8dca | ||
|
|
95fe2fe74b | ||
|
|
e937216cf7 | ||
|
|
fd8d93663e | ||
|
|
a048cfc102 | ||
|
|
272272634d | ||
|
|
01947f46b9 | ||
|
|
5ec73a81e9 | ||
|
|
4cba764ac3 | ||
|
|
84b9963609 | ||
|
|
99c216635f | ||
|
|
25a6fcc382 | ||
|
|
acefd3cacd | ||
|
|
15a5c87cc5 | ||
|
|
3044138e10 | ||
|
|
6fbb8ccf71 | ||
|
|
018866aa81 | ||
|
|
18b2b572ac | ||
|
|
d9e67aa5ee | ||
|
|
e675ce44d5 | ||
|
|
df46a8ea0d | ||
|
|
e277d5b55e | ||
|
|
4bd1d899db | ||
|
|
3e3d92c2e4 | ||
|
|
55deedd3b8 | ||
|
|
8b6251eb23 | ||
|
|
35919da0a4 | ||
|
|
ec018d2739 | ||
|
|
b1a23a13d6 | ||
|
|
3ebbe96786 | ||
|
|
cfaa4c60c9 | ||
|
|
e104450fc8 | ||
|
|
c22b0f4da8 | ||
|
|
302d0b58cc | ||
|
|
72999f0acb | ||
|
|
a942322d99 | ||
|
|
8902502c4d | ||
|
|
8d19741f53 | ||
|
|
e60549d764 | ||
|
|
0a42a28eeb | ||
|
|
88355d7eb4 | ||
|
|
67db5917ac | ||
|
|
8f1daf5d77 | ||
|
|
6d5e3b2a17 | ||
|
|
75c0d5089f | ||
|
|
5b1fcbfc51 | ||
|
|
0c80255f3a | ||
|
|
121f8e9174 | ||
|
|
c1cb50b39f | ||
|
|
791a740f32 | ||
|
|
546cdd7d8f | ||
|
|
8ef6602f82 | ||
|
|
2938759db4 | ||
|
|
f7bcca8553 | ||
|
|
4eaf893492 | ||
|
|
0ba6ab198a | ||
|
|
49a530f2b4 | ||
|
|
c14d3d7963 | ||
|
|
fe26d6ecec | ||
|
|
35a00b449d | ||
|
|
1e9c09d23a | ||
|
|
ec4f8a7f15 | ||
|
|
d109bf7b56 | ||
|
|
4625ab917d | ||
|
|
7d239f8990 | ||
|
|
22f7efd977 | ||
|
|
2a349673b9 | ||
|
|
361019fc6a | ||
|
|
abb099745f | ||
|
|
046edbbd54 | ||
|
|
8cc4c8c5e2 | ||
|
|
d2d3a3f216 | ||
|
|
cbf5c920ea | ||
|
|
7ac1d1f14c | ||
|
|
e811b87679 | ||
|
|
d08fca4e0a | ||
|
|
30aa41a2ee | ||
|
|
9d8d690100 | ||
|
|
43541d3028 | ||
|
|
880e831e71 | ||
|
|
fe2278b320 | ||
|
|
f7a8bbbcc8 | ||
|
|
fde4bc99aa | ||
|
|
cc9d6d0cf7 | ||
|
|
b17bbc9b86 | ||
|
|
d4c5349e1a | ||
|
|
2865a3c047 | ||
|
|
522fc7412c | ||
|
|
7ec3f86efb | ||
|
|
c11539cccb | ||
|
|
09488b8df3 | ||
|
|
e16cc493d2 | ||
|
|
379fc8961e | ||
|
|
43114f176b | ||
|
|
f01d959f7c | ||
|
|
28f4409768 | ||
|
|
364aecda2a | ||
|
|
9bfbac6be5 | ||
|
|
db20fd9eeb | ||
|
|
53a198fde8 | ||
|
|
861a13501a | ||
|
|
0f24bd2ea4 | ||
|
|
9dc54815eb | ||
|
|
cb8ca619ae | ||
|
|
ac91deac96 | ||
|
|
71e2be5d3d | ||
|
|
4878073867 | ||
|
|
dc327ddddb | ||
|
|
e6467403ef | ||
|
|
cf0fab2d66 | ||
|
|
2eaa03539c | ||
|
|
b7e49dc88d | ||
|
|
ca05455aa0 | ||
|
|
7ae3eb8321 | ||
|
|
ae54babbbb | ||
|
|
a8378fe84b | ||
|
|
85df0b6a67 | ||
|
|
95967b9c79 | ||
|
|
8396b999a2 | ||
|
|
3bbe82057c | ||
|
|
0c49fce78f | ||
|
|
f9af8ba1a0 | ||
|
|
80f91b5fa5 | ||
|
|
6478e8f5c7 | ||
|
|
55dc42e147 | ||
|
|
f55ab0d689 | ||
|
|
fe79c9b01e | ||
|
|
6291e90f9c | ||
|
|
a32fefb597 | ||
|
|
3e04d045ee | ||
|
|
fb4645a3dc | ||
|
|
e0eac89e5a | ||
|
|
90d34bfd06 | ||
|
|
e58b5762f3 | ||
|
|
3fd4d23bed | ||
|
|
75bd8d4627 | ||
|
|
e455cbf319 | ||
|
|
cee2d69718 | ||
|
|
ff17123123 | ||
|
|
e118a06a09 | ||
|
|
4898d74c6d | ||
|
|
be0c359160 | ||
|
|
3f792030d3 | ||
|
|
333ad0efac |
No files matched your search
+10
-8
@@ -1,19 +1,21 @@
|
|||||||
language: java
|
language: java
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- MAVEN_SKIP_RC=true
|
||||||
|
- MAVEN_OPTS="-Xms512m -Xmx2048m"
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk8
|
- oraclejdk8
|
||||||
|
|
||||||
cache:
|
before_script:
|
||||||
directories:
|
- export MAVEN_SKIP_RC=true
|
||||||
- $HOME/.m2
|
|
||||||
|
|
||||||
before_cache:
|
install:
|
||||||
- rm -rf $HOME/.m2/repository/org/keycloak
|
- mvn install -Pdistribution -DskipTests=true -B -V -q
|
||||||
|
|
||||||
install: mvn install -Pdistribution -DskipTests=true -B -V
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- mvn test -B
|
- mvn test -B
|
||||||
- mvn -file testsuite/integration-arquillian test -B
|
- mvn -file testsuite/integration-arquillian test -B -Pno-console
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
@@ -7,7 +7,7 @@ Keycloak is an SSO Service for web apps and REST services. For more information
|
|||||||
Building
|
Building
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Ensure you have JDK 7 (or newer), Maven 3.2.1 (or newer) and Git installed
|
Ensure you have JDK 8 (or newer), Maven 3.2.1 (or newer) and Git installed
|
||||||
|
|
||||||
java -version
|
java -version
|
||||||
mvn -version
|
mvn -version
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
@@ -92,4 +92,9 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
|||||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityProviderDataMarshaller getMarshaller() {
|
||||||
|
return new DefaultDataMarshaller();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+6
-1
@@ -1,10 +1,10 @@
|
|||||||
package org.keycloak.broker.provider;
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
@@ -35,4 +35,9 @@ public abstract class AbstractIdentityProviderMapper implements IdentityProvider
|
|||||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,13 @@
|
|||||||
package org.keycloak.broker.provider;
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,6 +156,22 @@ public class BrokeredIdentityContext {
|
|||||||
this.contextData = contextData;
|
this.contextData = contextData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the attribute, which will be available on "Update profile" page and in authenticators
|
||||||
|
public void setUserAttribute(String attributeName, String attributeValue) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
list.add(attributeValue);
|
||||||
|
getContextData().put(Constants.USER_ATTRIBUTES_PREFIX + attributeName, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserAttribute(String attributeName) {
|
||||||
|
List<String> userAttribute = (List<String>) getContextData().get(Constants.USER_ATTRIBUTES_PREFIX + attributeName);
|
||||||
|
if (userAttribute == null || userAttribute.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return userAttribute.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Base64Url;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class DefaultDataMarshaller implements IdentityProviderDataMarshaller {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serialize(Object value) {
|
||||||
|
if (value instanceof String) {
|
||||||
|
return (String) value;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
byte[] bytes = JsonSerialization.writeValueAsBytes(value);
|
||||||
|
return Base64Url.encode(bytes);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T deserialize(String serialized, Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
if (clazz.equals(String.class)) {
|
||||||
|
return clazz.cast(serialized);
|
||||||
|
} else {
|
||||||
|
byte[] bytes = Base64Url.decode(serialized);
|
||||||
|
if (List.class.isAssignableFrom(clazz)) {
|
||||||
|
List list = JsonSerialization.readValue(bytes, List.class);
|
||||||
|
return clazz.cast(list);
|
||||||
|
} else {
|
||||||
|
return JsonSerialization.readValue(bytes, clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -69,10 +69,10 @@ public class HardcodedAttributeMapper extends AbstractIdentityProviderMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
|
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
|
||||||
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
|
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
|
||||||
user.setSingleAttribute(attribute, attributeValue);
|
context.setUserAttribute(attribute, attributeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package org.keycloak.broker.provider;
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
|
||||||
import org.keycloak.models.ClientModel;
|
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -20,7 +17,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
||||||
public static final String ROLE = "role";
|
public static final String ROLE = "role";
|
||||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ProviderConfigProperty property;
|
ProviderConfigProperty property;
|
||||||
@@ -32,34 +29,6 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
|||||||
configProperties.add(property);
|
configProperties.add(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static String[] parseRole(String role) {
|
|
||||||
int scopeIndex = role.lastIndexOf('.');
|
|
||||||
if (scopeIndex > -1) {
|
|
||||||
String appName = role.substring(0, scopeIndex);
|
|
||||||
role = role.substring(scopeIndex + 1);
|
|
||||||
String[] rtn = {appName, role};
|
|
||||||
return rtn;
|
|
||||||
} else {
|
|
||||||
String[] rtn = {null, role};
|
|
||||||
return rtn;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RoleModel getRoleFromString(RealmModel realm, String roleName) {
|
|
||||||
String[] parsedRole = parseRole(roleName);
|
|
||||||
RoleModel role = null;
|
|
||||||
if (parsedRole[0] == null) {
|
|
||||||
role = realm.getRole(parsedRole[1]);
|
|
||||||
} else {
|
|
||||||
ClientModel client = realm.getClientByClientId(parsedRole[0]);
|
|
||||||
role = client.getRole(parsedRole[1]);
|
|
||||||
}
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
return configProperties;
|
return configProperties;
|
||||||
@@ -93,7 +62,7 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
|||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String roleName = mapperModel.getConfig().get(ROLE);
|
String roleName = mapperModel.getConfig().get(ROLE);
|
||||||
RoleModel role = getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
user.grantRole(role);
|
user.grantRole(role);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -67,7 +67,7 @@ public class HardcodedUserSessionAttributeMapper extends AbstractIdentityProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
|
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
|
||||||
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
|
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
|
||||||
context.getClientSession().setUserSessionNote(attribute, attributeValue);
|
context.getClientSession().setUserSessionNote(attribute, attributeValue);
|
||||||
|
|||||||
@@ -103,4 +103,10 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
|||||||
*/
|
*/
|
||||||
Response export(UriInfo uriInfo, RealmModel realm, String format);
|
Response export(UriInfo uriInfo, RealmModel realm, String format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of marshaller to serialize/deserialize attached data to Strings, which can be saved in clientSession
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
IdentityProviderDataMarshaller getMarshaller();
|
||||||
|
|
||||||
}
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package org.keycloak.broker.provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public interface IdentityProviderDataMarshaller {
|
||||||
|
|
||||||
|
String serialize(Object obj);
|
||||||
|
<T> T deserialize(String serialized, Class<T> clazz);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,8 +20,10 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
|
|||||||
String getDisplayType();
|
String getDisplayType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to determine what keycloak username and email to use to process the login request from the external IDP
|
* Called to determine what keycloak username and email to use to process the login request from the external IDP.
|
||||||
* Usually used to map BrokeredIdentityContet.username or email.
|
* It's called before "FirstBrokerLogin" flow, so can be used to map attributes to BrokeredIdentityContext ( BrokeredIdentityContext.setUserAttribute ),
|
||||||
|
* which will be available on "Review Profile" page and in authenticators during FirstBrokerLogin flow
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* @param session
|
* @param session
|
||||||
* @param realm
|
* @param realm
|
||||||
@@ -31,7 +33,7 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
|
|||||||
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after UserModel is created for first time for this user.
|
* Called after UserModel is created for first time for this user. Called after "FirstBrokerLogin" flow
|
||||||
*
|
*
|
||||||
* @param session
|
* @param session
|
||||||
* @param realm
|
* @param realm
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.keycloak.broker.provider.util;
|
package org.keycloak.broker.provider.util;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@@ -24,6 +27,9 @@ public class SimpleHttp {
|
|||||||
private Map<String, String> headers;
|
private Map<String, String> headers;
|
||||||
private Map<String, String> params;
|
private Map<String, String> params;
|
||||||
|
|
||||||
|
private SSLSocketFactory sslFactory;
|
||||||
|
private HostnameVerifier hostnameVerifier;
|
||||||
|
|
||||||
protected SimpleHttp(String url, String method) {
|
protected SimpleHttp(String url, String method) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
@@ -53,6 +59,15 @@ public class SimpleHttp {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SimpleHttp sslFactory(SSLSocketFactory factory) {
|
||||||
|
sslFactory = factory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleHttp hostnameVerifier(HostnameVerifier verifier) {
|
||||||
|
hostnameVerifier = verifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String asString() throws IOException {
|
public String asString() throws IOException {
|
||||||
boolean get = method.equals("GET");
|
boolean get = method.equals("GET");
|
||||||
@@ -85,6 +100,7 @@ public class SimpleHttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
setupTruststoreIfApplicable(connection);
|
||||||
OutputStream os = null;
|
OutputStream os = null;
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
|
|
||||||
@@ -171,6 +187,7 @@ public class SimpleHttp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
setupTruststoreIfApplicable(connection);
|
||||||
OutputStream os = null;
|
OutputStream os = null;
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
|
|
||||||
@@ -235,4 +252,13 @@ public class SimpleHttp {
|
|||||||
return writer.toString();
|
return writer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupTruststoreIfApplicable(HttpURLConnection connection) {
|
||||||
|
if (connection instanceof HttpsURLConnection && sslFactory != null) {
|
||||||
|
HttpsURLConnection con = (HttpsURLConnection) connection;
|
||||||
|
con.setSSLSocketFactory(sslFactory);
|
||||||
|
if (hostnameVerifier != null) {
|
||||||
|
con.setHostnameVerifier(hostnameVerifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+6
-2
@@ -20,13 +20,14 @@ package org.keycloak.broker.oidc;
|
|||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
import org.keycloak.connections.truststore.JSSETruststoreConfigurator;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
@@ -247,12 +248,15 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SimpleHttp generateTokenRequest(String authorizationCode) {
|
public SimpleHttp generateTokenRequest(String authorizationCode) {
|
||||||
|
JSSETruststoreConfigurator configurator = new JSSETruststoreConfigurator(session);
|
||||||
return SimpleHttp.doPost(getConfig().getTokenUrl())
|
return SimpleHttp.doPost(getConfig().getTokenUrl())
|
||||||
.param(OAUTH2_PARAMETER_CODE, authorizationCode)
|
.param(OAUTH2_PARAMETER_CODE, authorizationCode)
|
||||||
.param(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
|
.param(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
|
||||||
.param(OAUTH2_PARAMETER_CLIENT_SECRET, getConfig().getClientSecret())
|
.param(OAUTH2_PARAMETER_CLIENT_SECRET, getConfig().getClientSecret())
|
||||||
.param(OAUTH2_PARAMETER_REDIRECT_URI, uriInfo.getAbsolutePath().toString())
|
.param(OAUTH2_PARAMETER_REDIRECT_URI, uriInfo.getAbsolutePath().toString())
|
||||||
.param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE);
|
.param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE)
|
||||||
|
.sslFactory(configurator.getSSLSocketFactory())
|
||||||
|
.hostnameVerifier(configurator.getHostnameVerifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-1
@@ -5,6 +5,7 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
|
|||||||
import org.keycloak.constants.AdapterConstants;
|
import org.keycloak.constants.AdapterConstants;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
|
import org.keycloak.jose.jws.JWSInputException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
@@ -51,7 +52,13 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
|||||||
@POST
|
@POST
|
||||||
@Path(AdapterConstants.K_LOGOUT)
|
@Path(AdapterConstants.K_LOGOUT)
|
||||||
public Response backchannelLogout(String input) {
|
public Response backchannelLogout(String input) {
|
||||||
JWSInput token = new JWSInput(input);
|
JWSInput token = null;
|
||||||
|
try {
|
||||||
|
token = new JWSInput(input);
|
||||||
|
} catch (JWSInputException e) {
|
||||||
|
logger.warn("Failed to verify logout request");
|
||||||
|
return Response.status(400).build();
|
||||||
|
}
|
||||||
PublicKey key = getExternalIdpKey();
|
PublicKey key = getExternalIdpKey();
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
if (!verify(token, key)) {
|
if (!verify(token, key)) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.keycloak.events.Errors;
|
|||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
|
import org.keycloak.jose.jws.JWSInputException;
|
||||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
@@ -42,7 +43,7 @@ import org.keycloak.services.messages.Messages;
|
|||||||
import org.keycloak.services.resources.IdentityBrokerService;
|
import org.keycloak.services.resources.IdentityBrokerService;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.util.PemUtils;
|
import org.keycloak.common.util.PemUtils;
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
@@ -282,40 +283,41 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
|||||||
throw new IdentityBrokerException("No token from server.");
|
throw new IdentityBrokerException("No token from server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonWebToken token;
|
||||||
try {
|
try {
|
||||||
JWSInput jws = new JWSInput(encodedToken);
|
JWSInput jws = new JWSInput(encodedToken);
|
||||||
if (!verify(jws, key)) {
|
if (!verify(jws, key)) {
|
||||||
throw new IdentityBrokerException("token signature validation failed");
|
throw new IdentityBrokerException("token signature validation failed");
|
||||||
}
|
}
|
||||||
JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
|
token = jws.readJsonContent(JsonWebToken.class);
|
||||||
|
} catch (JWSInputException e) {
|
||||||
String iss = token.getIssuer();
|
throw new IdentityBrokerException("Invalid token", e);
|
||||||
|
|
||||||
if (!token.hasAudience(getConfig().getClientId())) {
|
|
||||||
throw new IdentityBrokerException("Wrong audience from token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!token.isActive()) {
|
|
||||||
throw new IdentityBrokerException("Token is no longer valid");
|
|
||||||
}
|
|
||||||
|
|
||||||
String trustedIssuers = getConfig().getIssuer();
|
|
||||||
|
|
||||||
if (trustedIssuers != null) {
|
|
||||||
String[] issuers = trustedIssuers.split(",");
|
|
||||||
|
|
||||||
for (String trustedIssuer : issuers) {
|
|
||||||
if (iss != null && iss.equals(trustedIssuer.trim())) {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IdentityBrokerException("Wrong issuer from token. Got: " + iss + " expected: " + getConfig().getIssuer());
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IdentityBrokerException("Could not decode token.", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String iss = token.getIssuer();
|
||||||
|
|
||||||
|
if (!token.hasAudience(getConfig().getClientId())) {
|
||||||
|
throw new IdentityBrokerException("Wrong audience from token.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token.isActive()) {
|
||||||
|
throw new IdentityBrokerException("Token is no longer valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
String trustedIssuers = getConfig().getIssuer();
|
||||||
|
|
||||||
|
if (trustedIssuers != null) {
|
||||||
|
String[] issuers = trustedIssuers.split(",");
|
||||||
|
|
||||||
|
for (String trustedIssuer : issuers) {
|
||||||
|
if (iss != null && iss.equals(trustedIssuer.trim())) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IdentityBrokerException("Wrong issuer from token. Got: " + iss + " expected: " + getConfig().getIssuer());
|
||||||
|
}
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
|
|||||||
} else if (value instanceof List) {
|
} else if (value instanceof List) {
|
||||||
List list = (List)value;
|
List list = (List)value;
|
||||||
for (Object val : list) {
|
for (Object val : list) {
|
||||||
return valueEquals(desiredValue, val);
|
if (valueEquals(desiredValue, val)) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+8
-8
@@ -69,8 +69,8 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||||||
* @param user context to store profile data into
|
* @param user context to store profile data into
|
||||||
* @param profile to store into context
|
* @param profile to store into context
|
||||||
* @param provider identification of social provider to be used in log dump
|
* @param provider identification of social provider to be used in log dump
|
||||||
*
|
*
|
||||||
* @see #importNewUser(KeycloakSession, RealmModel, UserModel, IdentityProviderMapperModel, BrokeredIdentityContext)
|
* @see #preprocessFederatedIdentity(KeycloakSession, RealmModel, IdentityProviderMapperModel, BrokeredIdentityContext)
|
||||||
* @see BrokeredIdentityContext#getContextData()
|
* @see BrokeredIdentityContext#getContextData()
|
||||||
*/
|
*/
|
||||||
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
|
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
|
||||||
@@ -100,17 +100,17 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
|
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
|
||||||
if (attribute == null || attribute.trim().isEmpty()) {
|
if (attribute == null || attribute.trim().isEmpty()) {
|
||||||
logger.debug("Attribute is not configured");
|
logger.warnf("Attribute is not configured for mapper %s", mapperModel.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
attribute = attribute.trim();
|
attribute = attribute.trim();
|
||||||
|
|
||||||
String value = getJsonValue(mapperModel, context);
|
String value = getJsonValue(mapperModel, context);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
user.setSingleAttribute(attribute, value);
|
context.setUserAttribute(attribute, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,13 +123,13 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||||||
|
|
||||||
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
|
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
|
||||||
if (jsonField == null || jsonField.trim().isEmpty()) {
|
if (jsonField == null || jsonField.trim().isEmpty()) {
|
||||||
logger.debug("JSON field path is not configured");
|
logger.warnf("JSON field path is not configured for mapper %s", mapperModel.getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
jsonField = jsonField.trim();
|
jsonField = jsonField.trim();
|
||||||
|
|
||||||
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
|
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
|
||||||
logger.debug("JSON field path is invalid " + jsonField);
|
logger.warnf("JSON field path is invalid %s", jsonField);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
|
|||||||
String value = getJsonValue(profileJsonNode, jsonField);
|
String value = getJsonValue(profileJsonNode, jsonField);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
logger.debug("User profile JSON value '" + jsonField + "' is not available.");
|
logger.debugf("User profile JSON value '%s' is not available.", jsonField);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.keycloak.models.KeycloakSession;
|
|||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -80,7 +81,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||||
if (hasClaimValue(mapperModel, context)) {
|
if (hasClaimValue(mapperModel, context)) {
|
||||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
user.grantRole(role);
|
user.grantRole(role);
|
||||||
}
|
}
|
||||||
@@ -90,7 +91,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
|||||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||||
if (!hasClaimValue(mapperModel, context)) {
|
if (!hasClaimValue(mapperModel, context)) {
|
||||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
user.deleteRoleMapping(role);
|
user.deleteRoleMapping(role);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -11,6 +11,7 @@ import org.keycloak.models.KeycloakSession;
|
|||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.JsonWebToken;
|
import org.keycloak.representations.JsonWebToken;
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
||||||
//if (token == null) return;
|
//if (token == null) return;
|
||||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||||
String[] parseRole = HardcodedRoleMapper.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
|
String[] parseRole = KeycloakModelUtils.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
|
||||||
String externalRoleName = parseRole[1];
|
String externalRoleName = parseRole[1];
|
||||||
String claimName = null;
|
String claimName = null;
|
||||||
if (parseRole[0] == null) {
|
if (parseRole[0] == null) {
|
||||||
@@ -95,7 +96,7 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
|||||||
}
|
}
|
||||||
Object claim = getClaimValue(token, claimName);
|
Object claim = getClaimValue(token, claimName);
|
||||||
if (valueEquals(externalRoleName, claim)) {
|
if (valueEquals(externalRoleName, claim)) {
|
||||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ public class UserAttributeMapper extends AbstractClaimMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||||
Object value = getClaimValue(mapperModel, context);
|
Object value = getClaimValue(mapperModel, context);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
user.setSingleAttribute(attribute, value.toString());
|
context.setUserAttribute(attribute, value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,10 +65,6 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||||||
return "Username Template Importer";
|
return "Username Template Importer";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
+6
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -45,6 +45,11 @@
|
|||||||
<artifactId>jboss-logging</artifactId>
|
<artifactId>jboss-logging</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLEventReader;
|
||||||
|
|
||||||
|
import org.keycloak.broker.provider.DefaultDataMarshaller;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||||
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
|
import org.keycloak.saml.common.util.StaxUtil;
|
||||||
|
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||||
|
import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil;
|
||||||
|
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
|
||||||
|
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class SAMLDataMarshaller extends DefaultDataMarshaller {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serialize(Object obj) {
|
||||||
|
|
||||||
|
// Lame impl, but hopefully sufficient for now. See if something better is needed...
|
||||||
|
if (obj.getClass().getName().startsWith("org.keycloak.dom.saml")) {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (obj instanceof ResponseType) {
|
||||||
|
ResponseType responseType = (ResponseType) obj;
|
||||||
|
SAMLResponseWriter samlWriter = new SAMLResponseWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||||
|
samlWriter.write(responseType);
|
||||||
|
} else if (obj instanceof AssertionType) {
|
||||||
|
AssertionType assertion = (AssertionType) obj;
|
||||||
|
SAMLAssertionWriter samlWriter = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||||
|
samlWriter.write(assertion);
|
||||||
|
} else if (obj instanceof AuthnStatementType) {
|
||||||
|
AuthnStatementType authnStatement = (AuthnStatementType) obj;
|
||||||
|
SAMLAssertionWriter samlWriter = new SAMLAssertionWriter(StaxUtil.getXMLStreamWriter(bos));
|
||||||
|
samlWriter.write(authnStatement, true);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to serialize object of type " + obj.getClass().getName());
|
||||||
|
}
|
||||||
|
} catch (ProcessingException pe) {
|
||||||
|
throw new RuntimeException(pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(bos.toByteArray());
|
||||||
|
} else {
|
||||||
|
return super.serialize(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T deserialize(String serialized, Class<T> clazz) {
|
||||||
|
if (clazz.getName().startsWith("org.keycloak.dom.saml")) {
|
||||||
|
String xmlString = serialized;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (clazz.equals(ResponseType.class) || clazz.equals(AssertionType.class)) {
|
||||||
|
byte[] bytes = xmlString.getBytes();
|
||||||
|
InputStream is = new ByteArrayInputStream(bytes);
|
||||||
|
Object respType = new SAMLParser().parse(is);
|
||||||
|
return clazz.cast(respType);
|
||||||
|
} else if (clazz.equals(AuthnStatementType.class)) {
|
||||||
|
byte[] bytes = xmlString.getBytes();
|
||||||
|
InputStream is = new ByteArrayInputStream(bytes);
|
||||||
|
XMLEventReader xmlEventReader = new SAMLParser().createEventReader(is);
|
||||||
|
AuthnStatementType authnStatement = SAMLParserUtil.parseAuthnStatement(xmlEventReader);
|
||||||
|
return clazz.cast(authnStatement);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to deserialize object of type " + clazz.getName());
|
||||||
|
}
|
||||||
|
} catch (ParsingException pe) {
|
||||||
|
throw new RuntimeException(pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.deserialize(serialized, clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package org.keycloak.broker.saml;
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.ClientConnection;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.keycloak.VerificationException;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
import org.keycloak.broker.provider.IdentityProvider;
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
@@ -10,7 +11,6 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
|||||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
|
|
||||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||||
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
|
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
|
||||||
@@ -24,36 +24,29 @@ import org.keycloak.events.EventType;
|
|||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
|
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
||||||
import org.keycloak.protocol.saml.SAMLRequestParser;
|
import org.keycloak.saml.SAML2LogoutResponseBuilder;
|
||||||
|
import org.keycloak.saml.SAMLRequestParser;
|
||||||
import org.keycloak.protocol.saml.SamlProtocol;
|
import org.keycloak.protocol.saml.SamlProtocol;
|
||||||
import org.keycloak.protocol.saml.SamlProtocolUtils;
|
import org.keycloak.protocol.saml.SamlProtocolUtils;
|
||||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
import org.keycloak.saml.common.util.DocumentUtil;
|
|
||||||
import org.keycloak.saml.common.util.StaxParserUtil;
|
|
||||||
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
|
|
||||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
|
||||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||||
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
|
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
||||||
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
|
|
||||||
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
||||||
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
||||||
import org.keycloak.services.ErrorPage;
|
import org.keycloak.services.ErrorPage;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.FormParam;
|
import javax.ws.rs.FormParam;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
@@ -61,9 +54,7 @@ import javax.ws.rs.core.MediaType;
|
|||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.xml.namespace.QName;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -106,6 +97,13 @@ public class SAMLEndpoint {
|
|||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Path("descriptor")
|
||||||
|
public Response getSPDescriptor() {
|
||||||
|
return provider.export(uriInfo, realm, null);
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
|
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
|
||||||
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
|
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
|
||||||
@@ -155,7 +153,7 @@ public class SAMLEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getBindingType();
|
protected abstract String getBindingType();
|
||||||
protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
|
protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException;
|
||||||
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
|
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
|
||||||
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
|
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
|
||||||
protected PublicKey getIDPKey() {
|
protected PublicKey getIDPKey() {
|
||||||
@@ -188,7 +186,7 @@ public class SAMLEndpoint {
|
|||||||
}
|
}
|
||||||
if (config.isValidateSignature()) {
|
if (config.isValidateSignature()) {
|
||||||
try {
|
try {
|
||||||
verifySignature(holder);
|
verifySignature(GeneralConstants.SAML_REQUEST_KEY, holder);
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
logger.error("validation failed", e);
|
logger.error("validation failed", e);
|
||||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||||
@@ -247,16 +245,18 @@ public class SAMLEndpoint {
|
|||||||
builder.logoutRequestID(request.getID());
|
builder.logoutRequestID(request.getID());
|
||||||
builder.destination(config.getSingleLogoutServiceUrl());
|
builder.destination(config.getSingleLogoutServiceUrl());
|
||||||
builder.issuer(issuerURL);
|
builder.issuer(issuerURL);
|
||||||
builder.relayState(relayState);
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||||
|
.relayState(relayState);
|
||||||
if (config.isWantAuthnRequestsSigned()) {
|
if (config.isWantAuthnRequestsSigned()) {
|
||||||
builder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
||||||
|
.signatureAlgorithm(provider.getSignatureAlgorithm())
|
||||||
.signDocument();
|
.signDocument();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (config.isPostBindingResponse()) {
|
if (config.isPostBindingResponse()) {
|
||||||
return builder.postBinding().response();
|
return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
|
||||||
} else {
|
} else {
|
||||||
return builder.redirectBinding().response();
|
return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
|
||||||
}
|
}
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -274,7 +274,7 @@ public class SAMLEndpoint {
|
|||||||
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
|
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AssertionType assertion = getAssertion(responseType);
|
AssertionType assertion = AssertionUtil.getAssertion(responseType, realm.getPrivateKey());
|
||||||
SubjectType subject = assertion.getSubject();
|
SubjectType subject = assertion.getSubject();
|
||||||
SubjectType.STSubType subType = subject.getSubType();
|
SubjectType.STSubType subType = subject.getSubType();
|
||||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||||
@@ -334,22 +334,6 @@ public class SAMLEndpoint {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
|
|
||||||
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
|
|
||||||
|
|
||||||
if (assertions.isEmpty()) {
|
|
||||||
throw new IdentityBrokerException("No assertion from response.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
|
|
||||||
EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
|
|
||||||
|
|
||||||
if (encryptedAssertion != null) {
|
|
||||||
decryptAssertion(responseType, realm.getPrivateKey());
|
|
||||||
|
|
||||||
}
|
|
||||||
return responseType.getAssertions().get(0).getAssertion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response handleSamlResponse(String samlResponse, String relayState) {
|
public Response handleSamlResponse(String samlResponse, String relayState) {
|
||||||
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
|
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
|
||||||
@@ -363,7 +347,7 @@ public class SAMLEndpoint {
|
|||||||
}
|
}
|
||||||
if (config.isValidateSignature()) {
|
if (config.isValidateSignature()) {
|
||||||
try {
|
try {
|
||||||
verifySignature(holder);
|
verifySignature(GeneralConstants.SAML_RESPONSE_KEY, holder);
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
logger.error("validation failed", e);
|
logger.error("validation failed", e);
|
||||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||||
@@ -406,43 +390,14 @@ public class SAMLEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
|
|
||||||
SAML2Response saml2Response = new SAML2Response();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Document doc = saml2Response.convert(responseType);
|
|
||||||
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
|
|
||||||
|
|
||||||
if (enc == null) {
|
|
||||||
throw new IdentityBrokerException("No encrypted assertion found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
|
|
||||||
Document newDoc = DocumentUtil.createDocument();
|
|
||||||
Node importedNode = newDoc.importNode(enc, true);
|
|
||||||
newDoc.appendChild(importedNode);
|
|
||||||
|
|
||||||
Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
|
|
||||||
SAMLParser parser = new SAMLParser();
|
|
||||||
|
|
||||||
JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
|
|
||||||
AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
|
|
||||||
.getNodeAsStream(decryptedDocumentElement)));
|
|
||||||
|
|
||||||
responseType.replaceAssertion(oldID, new ResponseType.RTChoiceType(assertion));
|
|
||||||
|
|
||||||
return responseType;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IdentityBrokerException("Could not decrypt assertion.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class PostBinding extends Binding {
|
protected class PostBinding extends Binding {
|
||||||
@Override
|
@Override
|
||||||
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
|
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||||
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
|
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,9 +420,9 @@ public class SAMLEndpoint {
|
|||||||
|
|
||||||
protected class RedirectBinding extends Binding {
|
protected class RedirectBinding extends Binding {
|
||||||
@Override
|
@Override
|
||||||
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
|
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||||
PublicKey publicKey = getIDPKey();
|
PublicKey publicKey = getIDPKey();
|
||||||
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
|
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
|
|||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||||
@@ -33,20 +34,18 @@ import org.keycloak.models.ClientSessionModel;
|
|||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
||||||
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
|
import org.keycloak.saml.SAML2AuthnRequestBuilder;
|
||||||
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
|
import org.keycloak.saml.SAML2LogoutRequestBuilder;
|
||||||
|
import org.keycloak.saml.SAML2NameIDPolicyBuilder;
|
||||||
|
import org.keycloak.saml.SignatureAlgorithm;
|
||||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@@ -92,7 +91,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
.issuer(issuerURL)
|
.issuer(issuerURL)
|
||||||
.forceAuthn(getConfig().isForceAuthn())
|
.forceAuthn(getConfig().isForceAuthn())
|
||||||
.protocolBinding(protocolBinding)
|
.protocolBinding(protocolBinding)
|
||||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
|
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
||||||
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||||
.relayState(request.getState());
|
.relayState(request.getState());
|
||||||
|
|
||||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||||
@@ -109,14 +109,15 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
|
|
||||||
KeyPair keypair = new KeyPair(publicKey, privateKey);
|
KeyPair keypair = new KeyPair(publicKey, privateKey);
|
||||||
|
|
||||||
authnRequestBuilder.signWith(keypair);
|
binding.signWith(keypair);
|
||||||
authnRequestBuilder.signDocument();
|
binding.signatureAlgorithm(getSignatureAlgorithm());
|
||||||
|
binding.signDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getConfig().isPostBindingAuthnRequest()) {
|
if (getConfig().isPostBindingAuthnRequest()) {
|
||||||
return authnRequestBuilder.postBinding().request();
|
return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
|
||||||
} else {
|
} else {
|
||||||
return authnRequestBuilder.redirectBinding().request();
|
return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could not create authentication request.", e);
|
throw new IdentityBrokerException("Could not create authentication request.", e);
|
||||||
@@ -153,9 +154,10 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
|
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
|
||||||
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
|
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
|
||||||
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
||||||
|
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
|
||||||
try {
|
try {
|
||||||
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
|
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
|
||||||
.param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
|
.param(GeneralConstants.SAML_REQUEST_KEY, binding.postBinding(logoutBuilder.buildDocument()).encoded())
|
||||||
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
|
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
|
||||||
boolean success = status >=200 && status < 400;
|
boolean success = status >=200 && status < 400;
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@@ -178,7 +180,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
||||||
return logoutBuilder.postBinding().request();
|
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(userSession, realm);
|
||||||
|
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -192,13 +195,19 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
.issuer(getEntityId(uriInfo, realm))
|
.issuer(getEntityId(uriInfo, realm))
|
||||||
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
|
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
|
||||||
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
|
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
|
||||||
.destination(singleLogoutServiceUrl)
|
.destination(singleLogoutServiceUrl);
|
||||||
|
return logoutBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JaxrsSAML2BindingBuilder buildLogoutBinding(UserSessionModel userSession, RealmModel realm) {
|
||||||
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||||
.relayState(userSession.getId());
|
.relayState(userSession.getId());
|
||||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||||
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
||||||
|
.signatureAlgorithm(getSignatureAlgorithm())
|
||||||
.signDocument();
|
.signDocument();
|
||||||
}
|
}
|
||||||
return logoutBuilder;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -220,7 +229,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
|
|
||||||
|
|
||||||
String descriptor =
|
String descriptor =
|
||||||
"<EntityDescriptor entityID=\"" + getEntityId(uriInfo, realm) + "\">\n" +
|
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + getEntityId(uriInfo, realm) + "\">\n" +
|
||||||
" <SPSSODescriptor AuthnRequestsSigned=\"" + getConfig().isWantAuthnRequestsSigned() + "\"\n" +
|
" <SPSSODescriptor AuthnRequestsSigned=\"" + getConfig().isWantAuthnRequestsSigned() + "\"\n" +
|
||||||
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
|
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
|
||||||
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
|
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
|
||||||
@@ -245,4 +254,18 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||||||
"</EntityDescriptor>\n";
|
"</EntityDescriptor>\n";
|
||||||
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SignatureAlgorithm getSignatureAlgorithm() {
|
||||||
|
String alg = getConfig().getSignatureAlgorithm();
|
||||||
|
if (alg != null) {
|
||||||
|
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
|
||||||
|
if (algorithm != null) return algorithm;
|
||||||
|
}
|
||||||
|
return SignatureAlgorithm.RSA_SHA256;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityProviderDataMarshaller getMarshaller() {
|
||||||
|
return new SAMLDataMarshaller();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +87,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||||||
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSignatureAlgorithm() {
|
||||||
|
return getConfig().get("signatureAlgorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignatureAlgorithm(String signatureAlgorithm) {
|
||||||
|
getConfig().put("signatureAlgorithm", signatureAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
public String getEncryptionPublicKey() {
|
public String getEncryptionPublicKey() {
|
||||||
return getConfig().get("encryptionPublicKey");
|
return getConfig().get("encryptionPublicKey");
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-65
@@ -73,75 +73,79 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
|||||||
List<EntityDescriptorType.EDTChoiceType> choiceType = entityType.getChoiceType();
|
List<EntityDescriptorType.EDTChoiceType> choiceType = entityType.getChoiceType();
|
||||||
|
|
||||||
if (!choiceType.isEmpty()) {
|
if (!choiceType.isEmpty()) {
|
||||||
EntityDescriptorType.EDTChoiceType edtChoiceType = choiceType.get(0);
|
IDPSSODescriptorType idpDescriptor = null;
|
||||||
List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = edtChoiceType.getDescriptors();
|
|
||||||
|
|
||||||
if (!descriptors.isEmpty()) {
|
//Metadata documents can contain multiple Descriptors (See ADFS metadata documents) such as RoleDescriptor, SPSSODescriptor, IDPSSODescriptor.
|
||||||
EntityDescriptorType.EDTDescriptorChoiceType edtDescriptorChoiceType = descriptors.get(0);
|
//So we need to loop through to find the IDPSSODescriptor.
|
||||||
IDPSSODescriptorType idpDescriptor = edtDescriptorChoiceType.getIdpDescriptor();
|
for(EntityDescriptorType.EDTChoiceType edtChoiceType : entityType.getChoiceType()) {
|
||||||
|
List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = edtChoiceType.getDescriptors();
|
||||||
|
|
||||||
if (idpDescriptor != null) {
|
if(!descriptors.isEmpty() && descriptors.get(0).getIdpDescriptor() != null) {
|
||||||
SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig();
|
idpDescriptor = descriptors.get(0).getIdpDescriptor();
|
||||||
String singleSignOnServiceUrl = null;
|
|
||||||
boolean postBinding = false;
|
|
||||||
for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) {
|
|
||||||
if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
|
|
||||||
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
|
||||||
postBinding = true;
|
|
||||||
break;
|
|
||||||
} else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
|
|
||||||
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String singleLogoutServiceUrl = null;
|
|
||||||
for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) {
|
|
||||||
if (postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
|
|
||||||
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
|
||||||
break;
|
|
||||||
} else if (!postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
|
|
||||||
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
samlIdentityProviderConfig.setSingleLogoutServiceUrl(singleLogoutServiceUrl);
|
|
||||||
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
|
|
||||||
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
|
|
||||||
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
|
|
||||||
samlIdentityProviderConfig.setPostBindingResponse(postBinding);
|
|
||||||
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding);
|
|
||||||
|
|
||||||
List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor();
|
|
||||||
String defaultCertificate = null;
|
|
||||||
|
|
||||||
if (keyDescriptor != null) {
|
|
||||||
for (KeyDescriptorType keyDescriptorType : keyDescriptor) {
|
|
||||||
Element keyInfo = keyDescriptorType.getKeyInfo();
|
|
||||||
Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate"));
|
|
||||||
|
|
||||||
if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) {
|
|
||||||
samlIdentityProviderConfig.setSigningCertificate(x509KeyInfo.getTextContent());
|
|
||||||
} else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) {
|
|
||||||
samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent());
|
|
||||||
} else if (keyDescriptorType.getUse() == null) {
|
|
||||||
defaultCertificate = x509KeyInfo.getTextContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultCertificate != null) {
|
|
||||||
if (samlIdentityProviderConfig.getSigningCertificate() == null) {
|
|
||||||
samlIdentityProviderConfig.setSigningCertificate(defaultCertificate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (samlIdentityProviderConfig.getEncryptionPublicKey() == null) {
|
|
||||||
samlIdentityProviderConfig.setEncryptionPublicKey(defaultCertificate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return samlIdentityProviderConfig.getConfig();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (idpDescriptor != null) {
|
||||||
|
SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig();
|
||||||
|
String singleSignOnServiceUrl = null;
|
||||||
|
boolean postBinding = false;
|
||||||
|
for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) {
|
||||||
|
if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
|
||||||
|
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
||||||
|
postBinding = true;
|
||||||
|
break;
|
||||||
|
} else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
|
||||||
|
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String singleLogoutServiceUrl = null;
|
||||||
|
for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) {
|
||||||
|
if (postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
|
||||||
|
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
||||||
|
break;
|
||||||
|
} else if (!postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
|
||||||
|
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
samlIdentityProviderConfig.setSingleLogoutServiceUrl(singleLogoutServiceUrl);
|
||||||
|
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
|
||||||
|
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
|
||||||
|
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
|
||||||
|
samlIdentityProviderConfig.setPostBindingResponse(postBinding);
|
||||||
|
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding);
|
||||||
|
|
||||||
|
List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor();
|
||||||
|
String defaultCertificate = null;
|
||||||
|
|
||||||
|
if (keyDescriptor != null) {
|
||||||
|
for (KeyDescriptorType keyDescriptorType : keyDescriptor) {
|
||||||
|
Element keyInfo = keyDescriptorType.getKeyInfo();
|
||||||
|
Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate"));
|
||||||
|
|
||||||
|
if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) {
|
||||||
|
samlIdentityProviderConfig.setSigningCertificate(x509KeyInfo.getTextContent());
|
||||||
|
} else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) {
|
||||||
|
samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent());
|
||||||
|
} else if (keyDescriptorType.getUse() == null) {
|
||||||
|
defaultCertificate = x509KeyInfo.getTextContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultCertificate != null) {
|
||||||
|
if (samlIdentityProviderConfig.getSigningCertificate() == null) {
|
||||||
|
samlIdentityProviderConfig.setSigningCertificate(defaultCertificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samlIdentityProviderConfig.getEncryptionPublicKey() == null) {
|
||||||
|
samlIdentityProviderConfig.setEncryptionPublicKey(defaultCertificate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return samlIdentityProviderConfig.getConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ParsingException pe) {
|
} catch (ParsingException pe) {
|
||||||
throw new RuntimeException("Could not parse IdP SAML Metadata", pe);
|
throw new RuntimeException("Could not parse IdP SAML Metadata", pe);
|
||||||
|
|||||||
+3
-2
@@ -15,6 +15,7 @@ import org.keycloak.models.KeycloakSession;
|
|||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.JsonWebToken;
|
import org.keycloak.representations.JsonWebToken;
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
|||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||||
if (isAttributePresent(mapperModel, context)) {
|
if (isAttributePresent(mapperModel, context)) {
|
||||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
user.grantRole(role);
|
user.grantRole(role);
|
||||||
}
|
}
|
||||||
@@ -125,7 +126,7 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
|||||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||||
if (!isAttributePresent(mapperModel, context)) {
|
if (!isAttributePresent(mapperModel, context)) {
|
||||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
|
||||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||||
user.deleteRoleMapping(role);
|
user.deleteRoleMapping(role);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,11 +80,11 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||||
Object value = getAttribute(mapperModel, context);
|
Object value = getAttribute(mapperModel, context);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
user.setSingleAttribute(attribute, value.toString());
|
context.setUserAttribute(attribute, value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,11 +71,6 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
|||||||
return "Username Template Importer";
|
return "Username Template Importer";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class SAMLDataMarshallerTest {
|
||||||
|
|
||||||
|
private static final String TEST_RESPONSE = "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_4804cf50-cd96-4b92-823e-89adaa0c78ba\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.920Z\" Destination=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\" InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"></samlp:StatusCode></samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>";
|
||||||
|
|
||||||
|
private static final String TEST_ASSERTION = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
||||||
|
|
||||||
|
private static final String TEST_AUTHN_TYPE = "<saml:AuthnStatement xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement>";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseResponse() throws Exception {
|
||||||
|
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||||
|
ResponseType responseType = serializer.deserialize(TEST_RESPONSE, ResponseType.class);
|
||||||
|
|
||||||
|
// test ResponseType
|
||||||
|
Assert.assertEquals(responseType.getID(), "ID_4804cf50-cd96-4b92-823e-89adaa0c78ba");
|
||||||
|
Assert.assertEquals(responseType.getDestination(), "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint");
|
||||||
|
Assert.assertEquals(responseType.getIssuer().getValue(), "http://localhost:8082/auth/realms/realm-with-saml-idp-basic");
|
||||||
|
Assert.assertEquals(responseType.getAssertions().get(0).getID(), "ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9");
|
||||||
|
|
||||||
|
// back to String
|
||||||
|
String serialized = serializer.serialize(responseType);
|
||||||
|
Assert.assertEquals(TEST_RESPONSE, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseAssertion() throws Exception {
|
||||||
|
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||||
|
AssertionType assertion = serializer.deserialize(TEST_ASSERTION, AssertionType.class);
|
||||||
|
|
||||||
|
// test assertion
|
||||||
|
Assert.assertEquals(assertion.getID(), "ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9");
|
||||||
|
Assert.assertEquals(((NameIDType) assertion.getSubject().getSubType().getBaseID()).getValue(), "test-user");
|
||||||
|
|
||||||
|
// back to String
|
||||||
|
String serialized = serializer.serialize(assertion);
|
||||||
|
Assert.assertEquals(TEST_ASSERTION, serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseAuthnType() throws Exception {
|
||||||
|
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
|
||||||
|
AuthnStatementType authnStatement = serializer.deserialize(TEST_AUTHN_TYPE, AuthnStatementType.class);
|
||||||
|
|
||||||
|
// test authnStatement
|
||||||
|
Assert.assertEquals(authnStatement.getSessionIndex(), "fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5");
|
||||||
|
|
||||||
|
// back to String
|
||||||
|
String serialized = serializer.serialize(authnStatement);
|
||||||
|
Assert.assertEquals(TEST_AUTHN_TYPE, serialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<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-client-registration-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.8.0.CR2</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-client-registration-api</artifactId>
|
||||||
|
<name>Keycloak Client Registration API</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHeaders;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.keycloak.common.util.Base64;
|
||||||
|
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public abstract class Auth {
|
||||||
|
|
||||||
|
public abstract void addAuth(HttpRequest request);
|
||||||
|
|
||||||
|
public static Auth token(String token) {
|
||||||
|
return new BearerTokenAuth(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Auth token(ClientInitialAccessPresentation initialAccess) {
|
||||||
|
return new BearerTokenAuth(initialAccess.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Auth token(ClientRepresentation client) {
|
||||||
|
return new BearerTokenAuth(client.getRegistrationAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Auth token(OIDCClientRepresentation client) {
|
||||||
|
return new BearerTokenAuth(client.getRegistrationAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Auth client(String clientId, String clientSecret) {
|
||||||
|
return new BasicAuth(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BearerTokenAuth extends Auth {
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
public BearerTokenAuth(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuth(HttpRequest request) {
|
||||||
|
request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BasicAuth extends Auth {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public BasicAuth(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuth(HttpRequest request) {
|
||||||
|
String val = Base64.encodeBytes((username + ":" + password).getBytes());
|
||||||
|
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistration {
|
||||||
|
|
||||||
|
public static final ObjectMapper outputMapper = new ObjectMapper();
|
||||||
|
static {
|
||||||
|
outputMapper.getSerializationConfig().addMixInAnnotations(ClientRepresentation.class, ClientRepresentationMixIn.class);
|
||||||
|
outputMapper.getSerializationConfig().addMixInAnnotations(OIDCClientRepresentation.class, OIDCClientRepresentationMixIn.class);
|
||||||
|
outputMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String JSON = "application/json";
|
||||||
|
private final String XML = "application/xml";
|
||||||
|
|
||||||
|
private final String DEFAULT = "default";
|
||||||
|
private final String INSTALLATION = "install";
|
||||||
|
private final String OIDC = "openid-connect";
|
||||||
|
private final String SAML = "saml2-entity-descriptor";
|
||||||
|
|
||||||
|
private HttpUtil httpUtil;
|
||||||
|
|
||||||
|
public static ClientRegistrationBuilder create() {
|
||||||
|
return new ClientRegistrationBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientRegistration(HttpUtil httpUtil) {
|
||||||
|
this.httpUtil = httpUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws ClientRegistrationException {
|
||||||
|
if (httpUtil != null) {
|
||||||
|
httpUtil.close();
|
||||||
|
}
|
||||||
|
httpUtil = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistration auth(Auth auth) {
|
||||||
|
httpUtil.setAuth(auth);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
String content = serialize(client);
|
||||||
|
InputStream resultStream = httpUtil.doPost(content, JSON, JSON, DEFAULT);
|
||||||
|
return deserialize(resultStream, ClientRepresentation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRepresentation get(String clientId) throws ClientRegistrationException {
|
||||||
|
InputStream resultStream = httpUtil.doGet(JSON, DEFAULT, clientId);
|
||||||
|
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdapterConfig getAdapterConfig(String clientId) throws ClientRegistrationException {
|
||||||
|
InputStream resultStream = httpUtil.doGet(JSON, INSTALLATION, clientId);
|
||||||
|
return resultStream != null ? deserialize(resultStream, AdapterConfig.class) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRepresentation update(ClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
String content = serialize(client);
|
||||||
|
InputStream resultStream = httpUtil.doPut(content, JSON, JSON, DEFAULT, client.getClientId());
|
||||||
|
return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(ClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
delete(client.getClientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(String clientId) throws ClientRegistrationException {
|
||||||
|
httpUtil.doDelete(DEFAULT, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OIDCClientRegistration oidc() {
|
||||||
|
return new OIDCClientRegistration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SAMLClientRegistration saml() {
|
||||||
|
return new SAMLClientRegistration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serialize(Object obj) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
return outputMapper.writeValueAsString(obj);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to write json object", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T deserialize(InputStream inputStream, Class<T> clazz) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
return JsonSerialization.readValue(inputStream, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to read json object", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OIDCClientRegistration {
|
||||||
|
|
||||||
|
public OIDCClientRepresentation create(OIDCClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
String content = serialize(client);
|
||||||
|
InputStream resultStream = httpUtil.doPost(content, JSON, JSON, OIDC);
|
||||||
|
return deserialize(resultStream, OIDCClientRepresentation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OIDCClientRepresentation get(String clientId) throws ClientRegistrationException {
|
||||||
|
InputStream resultStream = httpUtil.doGet(JSON, OIDC, clientId);
|
||||||
|
return resultStream != null ? deserialize(resultStream, OIDCClientRepresentation.class) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OIDCClientRepresentation update(OIDCClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
String content = serialize(client);
|
||||||
|
InputStream resultStream = httpUtil.doPut(content, JSON, JSON, OIDC, client.getClientId());
|
||||||
|
return resultStream != null ? deserialize(resultStream, OIDCClientRepresentation.class) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(OIDCClientRepresentation client) throws ClientRegistrationException {
|
||||||
|
delete(client.getClientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(String clientId) throws ClientRegistrationException {
|
||||||
|
httpUtil.doDelete(OIDC, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SAMLClientRegistration {
|
||||||
|
|
||||||
|
public ClientRepresentation create(String entityDescriptor) throws ClientRegistrationException {
|
||||||
|
InputStream resultStream = httpUtil.doPost(entityDescriptor, XML, JSON, SAML);
|
||||||
|
return deserialize(resultStream, ClientRepresentation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ClientRegistrationBuilder {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
ClientRegistrationBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationBuilder url(String realmUrl) {
|
||||||
|
url = realmUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationBuilder url(String authUrl, String realm) {
|
||||||
|
url = HttpUtil.getUrl(authUrl, "realms", realm, "clients");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationBuilder httpClient(HttpClient httpClient) {
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistration build() {
|
||||||
|
if (url == null) {
|
||||||
|
throw new IllegalStateException("url not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpClient == null) {
|
||||||
|
httpClient = HttpClients.createDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClientRegistration(new HttpUtil(httpClient, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationException extends Exception {
|
||||||
|
|
||||||
|
public ClientRegistrationException(String s, Throwable throwable) {
|
||||||
|
super(s, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRegistrationException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
abstract class ClientRepresentationMixIn {
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
String registrationAccessToken;
|
||||||
|
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.apache.http.StatusLine;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class HttpErrorException extends IOException {
|
||||||
|
|
||||||
|
private StatusLine statusLine;
|
||||||
|
|
||||||
|
public HttpErrorException(StatusLine statusLine) {
|
||||||
|
this.statusLine = statusLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusLine getStatusLine() {
|
||||||
|
return statusLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHeaders;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.*;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.keycloak.client.registration.Auth;
|
||||||
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
|
import org.keycloak.client.registration.HttpErrorException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
class HttpUtil {
|
||||||
|
|
||||||
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
private String baseUri;
|
||||||
|
|
||||||
|
private Auth auth;
|
||||||
|
|
||||||
|
HttpUtil(HttpClient httpClient, String baseUri) {
|
||||||
|
this.httpClient = httpClient;
|
||||||
|
this.baseUri = baseUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAuth(Auth auth) {
|
||||||
|
this.auth = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream doPost(String content, String contentType, String acceptType, String... path) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
HttpPost request = new HttpPost(getUrl(baseUri, path));
|
||||||
|
|
||||||
|
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
|
request.setHeader(HttpHeaders.ACCEPT, acceptType);
|
||||||
|
request.setEntity(new StringEntity(content));
|
||||||
|
|
||||||
|
addAuth(request);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.execute(request);
|
||||||
|
InputStream responseStream = null;
|
||||||
|
if (response.getEntity() != null) {
|
||||||
|
responseStream = response.getEntity().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() == 201) {
|
||||||
|
return responseStream;
|
||||||
|
} else {
|
||||||
|
responseStream.close();
|
||||||
|
throw new HttpErrorException(response.getStatusLine());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to send request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream doGet(String acceptType, String... path) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
HttpGet request = new HttpGet(getUrl(baseUri, path));
|
||||||
|
|
||||||
|
request.setHeader(HttpHeaders.ACCEPT, acceptType);
|
||||||
|
|
||||||
|
addAuth(request);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.execute(request);
|
||||||
|
InputStream responseStream = null;
|
||||||
|
if (response.getEntity() != null) {
|
||||||
|
responseStream = response.getEntity().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() == 200) {
|
||||||
|
return responseStream;
|
||||||
|
} else if (response.getStatusLine().getStatusCode() == 404) {
|
||||||
|
responseStream.close();
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (responseStream != null) {
|
||||||
|
responseStream.close();
|
||||||
|
}
|
||||||
|
throw new HttpErrorException(response.getStatusLine());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to send request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream doPut(String content, String contentType, String acceptType, String... path) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
HttpPut request = new HttpPut(getUrl(baseUri, path));
|
||||||
|
|
||||||
|
request.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
|
request.setHeader(HttpHeaders.ACCEPT, acceptType);
|
||||||
|
request.setEntity(new StringEntity(content));
|
||||||
|
|
||||||
|
addAuth(request);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.execute(request);
|
||||||
|
if (response.getEntity() != null) {
|
||||||
|
response.getEntity().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream responseStream = null;
|
||||||
|
if (response.getEntity() != null) {
|
||||||
|
responseStream = response.getEntity().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() == 200) {
|
||||||
|
return responseStream;
|
||||||
|
} else {
|
||||||
|
if (responseStream != null) {
|
||||||
|
responseStream.close();
|
||||||
|
}
|
||||||
|
throw new HttpErrorException(response.getStatusLine());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to send request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doDelete(String... path) throws ClientRegistrationException {
|
||||||
|
try {
|
||||||
|
HttpDelete request = new HttpDelete(getUrl(baseUri, path));
|
||||||
|
|
||||||
|
addAuth(request);
|
||||||
|
|
||||||
|
HttpResponse response = httpClient.execute(request);
|
||||||
|
if (response.getEntity() != null) {
|
||||||
|
response.getEntity().getContent().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() != 204) {
|
||||||
|
throw new HttpErrorException(response.getStatusLine());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to send request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() throws ClientRegistrationException {
|
||||||
|
if (httpClient instanceof CloseableHttpClient) {
|
||||||
|
try {
|
||||||
|
((CloseableHttpClient) httpClient).close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClientRegistrationException("Failed to close http client", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getUrl(String baseUri, String... path) {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append(baseUri);
|
||||||
|
for (String p : path) {
|
||||||
|
s.append('/');
|
||||||
|
s.append(p);
|
||||||
|
}
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAuth(HttpRequestBase request) {
|
||||||
|
if (auth != null) {
|
||||||
|
auth.addAuth(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.keycloak.client.registration;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
abstract class OIDCClientRepresentationMixIn {
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Integer client_id_issued_at;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Integer client_secret_expires_at;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private String registration_client_uri;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private String registration_access_token;
|
||||||
|
|
||||||
|
}
|
||||||
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<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-client-registration-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.7.0.Final-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-client-registration-cli</artifactId>
|
||||||
|
<name>Keycloak Client Registration CLI</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-client-registration-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.aesh</groupId>
|
||||||
|
<artifactId>aesh</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
|
import org.jboss.aesh.cl.parser.CommandLineParserException;
|
||||||
|
import org.jboss.aesh.console.AeshConsole;
|
||||||
|
import org.jboss.aesh.console.AeshConsoleBuilder;
|
||||||
|
import org.jboss.aesh.console.Prompt;
|
||||||
|
import org.jboss.aesh.console.command.Command;
|
||||||
|
import org.jboss.aesh.console.command.CommandNotFoundException;
|
||||||
|
import org.jboss.aesh.console.command.registry.AeshCommandRegistryBuilder;
|
||||||
|
import org.jboss.aesh.console.settings.Settings;
|
||||||
|
import org.jboss.aesh.console.settings.SettingsBuilder;
|
||||||
|
import org.jboss.aesh.terminal.Color;
|
||||||
|
import org.jboss.aesh.terminal.TerminalColor;
|
||||||
|
import org.jboss.aesh.terminal.TerminalString;
|
||||||
|
import org.keycloak.client.registration.Auth;
|
||||||
|
import org.keycloak.client.registration.ClientRegistration;
|
||||||
|
import org.keycloak.client.registration.cli.commands.CreateCommand;
|
||||||
|
import org.keycloak.client.registration.cli.commands.ExitCommand;
|
||||||
|
import org.keycloak.client.registration.cli.commands.SetupCommand;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientRegistrationCLI {
|
||||||
|
|
||||||
|
private static ClientRegistration reg;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws CommandLineParserException, CommandNotFoundException {
|
||||||
|
reg = ClientRegistration.create().url("http://localhost:8080/auth/realms/master").build();
|
||||||
|
reg.auth(Auth.token("..."));
|
||||||
|
|
||||||
|
Context context = new Context();
|
||||||
|
|
||||||
|
List<Command> commands = new LinkedList<>();
|
||||||
|
commands.add(new SetupCommand(context));
|
||||||
|
commands.add(new CreateCommand(context));
|
||||||
|
commands.add(new ExitCommand(context));
|
||||||
|
|
||||||
|
SettingsBuilder builder = new SettingsBuilder().logging(true);
|
||||||
|
builder.enableMan(true).readInputrc(false);
|
||||||
|
|
||||||
|
Settings settings = builder.create();
|
||||||
|
|
||||||
|
AeshCommandRegistryBuilder commandRegistryBuilder = new AeshCommandRegistryBuilder();
|
||||||
|
for (Command c : commands) {
|
||||||
|
commandRegistryBuilder.command(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
AeshConsole aeshConsole = new AeshConsoleBuilder()
|
||||||
|
.commandRegistry(commandRegistryBuilder.create())
|
||||||
|
.settings(settings)
|
||||||
|
.prompt(new Prompt(new TerminalString("[clientreg]$ ",
|
||||||
|
new TerminalColor(Color.GREEN, Color.DEFAULT, Color.Intensity.BRIGHT))))
|
||||||
|
.create();
|
||||||
|
|
||||||
|
aeshConsole.start();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (args.length > 0) {
|
||||||
|
CommandContainer command = registry.getCommand(args[0], null);
|
||||||
|
ParserGenerator.parseAndPopulate(command, args[0], Arrays.copyOfRange(args, 1, args.length));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//commandInvocation.getCommandRegistry().getAllCommandNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.codehaus.jackson.map.SerializationConfig;
|
||||||
|
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||||
|
import org.keycloak.client.registration.ClientRegistration;
|
||||||
|
import org.keycloak.util.SystemPropertiesJsonParserFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class Context {
|
||||||
|
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper(new SystemPropertiesJsonParserFactory());
|
||||||
|
static {
|
||||||
|
mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
|
||||||
|
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientRegistration reg;
|
||||||
|
|
||||||
|
public ClientRegistration getReg() {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReg(ClientRegistration reg) {
|
||||||
|
this.reg = reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T readJson(InputStream bytes, Class<T> type) throws IOException {
|
||||||
|
return mapper.readValue(bytes, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
|
import org.jboss.aesh.cl.Arguments;
|
||||||
|
import org.jboss.aesh.cl.CommandDefinition;
|
||||||
|
import org.jboss.aesh.cl.Option;
|
||||||
|
import org.jboss.aesh.console.command.Command;
|
||||||
|
import org.jboss.aesh.console.command.CommandResult;
|
||||||
|
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||||
|
import org.jboss.aesh.io.Resource;
|
||||||
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
|
import org.keycloak.client.registration.cli.Context;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
@CommandDefinition(name="create", description = "[OPTIONS] FILE")
|
||||||
|
public class CreateCommand implements Command {
|
||||||
|
|
||||||
|
@Option(shortName = 'h', hasValue = false, description = "display this help and exit")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
@Arguments(description = "files or directories thats listed")
|
||||||
|
private List<Resource> arguments;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public CreateCommand(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult execute(CommandInvocation commandInvocation) throws IOException, InterruptedException {
|
||||||
|
System.out.println(help);
|
||||||
|
|
||||||
|
|
||||||
|
if(help) {
|
||||||
|
commandInvocation.getShell().out().println(commandInvocation.getHelpInfo("create"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if(arguments != null) {
|
||||||
|
for(Resource f : arguments) {
|
||||||
|
System.out.println(f.getAbsolutePath());
|
||||||
|
ClientRepresentation rep = JsonSerialization.readValue(f.read(), ClientRepresentation.class);
|
||||||
|
try {
|
||||||
|
context.getReg().create(rep);
|
||||||
|
} catch (ClientRegistrationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reg.create();
|
||||||
|
|
||||||
|
return CommandResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
|
import org.jboss.aesh.cl.CommandDefinition;
|
||||||
|
import org.jboss.aesh.console.command.Command;
|
||||||
|
import org.jboss.aesh.console.command.CommandResult;
|
||||||
|
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||||
|
import org.keycloak.client.registration.cli.Context;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
@CommandDefinition(name="exit", description = "Exit the program")
|
||||||
|
public class ExitCommand implements Command {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public ExitCommand(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult execute(CommandInvocation commandInvocation) throws IOException, InterruptedException {
|
||||||
|
commandInvocation.stop();
|
||||||
|
return CommandResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
|
import org.jboss.aesh.cl.CommandDefinition;
|
||||||
|
import org.jboss.aesh.cl.Option;
|
||||||
|
import org.jboss.aesh.console.command.Command;
|
||||||
|
import org.jboss.aesh.console.command.CommandResult;
|
||||||
|
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||||
|
import org.jboss.aesh.io.Resource;
|
||||||
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
|
import org.keycloak.client.registration.cli.Context;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
@CommandDefinition(name="setup", description = "")
|
||||||
|
public class SetupCommand implements Command {
|
||||||
|
|
||||||
|
@Option(shortName = 'h', hasValue = false, description = "display this help and exit")
|
||||||
|
private boolean help;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public SetupCommand(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult execute(CommandInvocation commandInvocation) throws IOException, InterruptedException {
|
||||||
|
System.out.println(help);
|
||||||
|
|
||||||
|
if(help) {
|
||||||
|
commandInvocation.getShell().out().println(commandInvocation.getHelpInfo("create"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String promptForUsername(CommandInvocation invocation) throws InterruptedException {
|
||||||
|
invocation.print("username: ");
|
||||||
|
return invocation.getInputLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
<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>1.8.0.CR2</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<name>Keycloak Client Registration Parent</name>
|
||||||
|
<description/>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>keycloak-client-registration-parent</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>api</module>
|
||||||
|
<!--<module>cli</module>-->
|
||||||
|
</modules>
|
||||||
|
</project>
|
||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<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>1.8.0.CR2</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-common</artifactId>
|
||||||
|
<name>Keycloak Common</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>Common library and dependencies shared with server and all adapters</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<timestamp>${maven.build.timestamp}</timestamp>
|
||||||
|
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
|
||||||
|
<keycloak.osgi.export>
|
||||||
|
org.keycloak.common.*
|
||||||
|
</keycloak.osgi.export>
|
||||||
|
<keycloak.osgi.import>
|
||||||
|
*;resolution:=optional
|
||||||
|
</keycloak.osgi.import>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>${maven.compiler.source}</source>
|
||||||
|
<target>${maven.compiler.target}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Adding OSGI metadata to the JAR without changing the packaging type. -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>bundle-manifest</id>
|
||||||
|
<phase>process-classes</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>manifest</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-ClassPath>.</Bundle-ClassPath>
|
||||||
|
<Bundle-Name>${project.name}</Bundle-Name>
|
||||||
|
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Import-Package>${keycloak.osgi.import}</Import-Package>
|
||||||
|
<Export-Package>${keycloak.osgi.export}</Export-Package>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak;
|
package org.keycloak.common;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the client connection
|
* Information about the client connection
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak;
|
package org.keycloak.common;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package org.keycloak.common;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class Version {
|
||||||
|
public static final String UNKNOWN = "UNKNOWN";
|
||||||
|
public static String NAME;
|
||||||
|
public static String NAME_HTML;
|
||||||
|
public static String VERSION;
|
||||||
|
public static String RESOURCES_VERSION;
|
||||||
|
public static String BUILD_TIME;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Properties props = new Properties();
|
||||||
|
InputStream is = Version.class.getResourceAsStream("/keycloak-version.properties");
|
||||||
|
try {
|
||||||
|
props.load(is);
|
||||||
|
Version.NAME = props.getProperty("name");
|
||||||
|
Version.NAME_HTML = props.getProperty("name-html");
|
||||||
|
Version.VERSION = props.getProperty("version");
|
||||||
|
Version.BUILD_TIME = props.getProperty("build-time");
|
||||||
|
Version.RESOURCES_VERSION = Version.VERSION.toLowerCase();
|
||||||
|
if (Version.RESOURCES_VERSION.endsWith("-snapshot")) {
|
||||||
|
Version.RESOURCES_VERSION = Version.RESOURCES_VERSION.replace("-snapshot", "-" + Time.currentTime());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Version.VERSION= Version.UNKNOWN;
|
||||||
|
Version.BUILD_TIME= Version.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.constants;
|
package org.keycloak.common.constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.constants;
|
package org.keycloak.common.constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.constants;
|
package org.keycloak.common.constants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.enums;
|
package org.keycloak.common.enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
package org.keycloak.enums;
|
package org.keycloak.common.enums;
|
||||||
|
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
File diff suppressed because it is too large.
Load diff
+1
-3
@@ -1,8 +1,6 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
|
||||||
import net.iharder.Base64;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
+1
-2
@@ -1,10 +1,9 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
+16
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -23,4 +23,19 @@ public class CollectionUtil {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if all items from col1 are in col2 and viceversa. Order is not taken into account
|
||||||
|
public static <T> boolean collectionEquals(Collection<T> col1, Collection<T> col2) {
|
||||||
|
if (col1.size() != col2.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (T item : col1) {
|
||||||
|
if (!col2.contains(item)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import org.keycloak.constants.GenericConstants;
|
import org.keycloak.common.constants.GenericConstants;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author pedroigor
|
* @author pedroigor
|
||||||
+3
-4
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -12,13 +12,12 @@ import java.lang.reflect.Method;
|
|||||||
|
|
||||||
import javax.security.auth.kerberos.KerberosTicket;
|
import javax.security.auth.kerberos.KerberosTicket;
|
||||||
|
|
||||||
import net.iharder.Base64;
|
|
||||||
import org.ietf.jgss.GSSCredential;
|
import org.ietf.jgss.GSSCredential;
|
||||||
import org.ietf.jgss.GSSException;
|
import org.ietf.jgss.GSSException;
|
||||||
import org.ietf.jgss.GSSManager;
|
import org.ietf.jgss.GSSManager;
|
||||||
import org.ietf.jgss.Oid;
|
import org.ietf.jgss.Oid;
|
||||||
import org.keycloak.constants.KerberosConstants;
|
import org.keycloak.common.constants.KerberosConstants;
|
||||||
import org.keycloak.util.reflections.Reflections;
|
import org.keycloak.common.util.reflections.Reflections;
|
||||||
import sun.security.jgss.GSSCredentialImpl;
|
import sun.security.jgss.GSSCredentialImpl;
|
||||||
import sun.security.jgss.GSSManagerImpl;
|
import sun.security.jgss.GSSManagerImpl;
|
||||||
import sun.security.jgss.krb5.Krb5InitCredential;
|
import sun.security.jgss.krb5.Krb5InitCredential;
|
||||||
+12
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -341,6 +341,17 @@ public class KeycloakUriBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fragment, but not encode it. It assumes that given fragment was already properly encoded
|
||||||
|
*
|
||||||
|
* @param fragment
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public KeycloakUriBuilder encodedFragment(String fragment) {
|
||||||
|
this.fragment = fragment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only replace path params in path of URI. This changes state of URIBuilder.
|
* Only replace path params in path of URI. This changes state of URIBuilder.
|
||||||
*
|
*
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -6,7 +6,7 @@ import java.io.InputStream;
|
|||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
|
||||||
import org.keycloak.constants.GenericConstants;
|
import org.keycloak.common.constants.GenericConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import javax.activation.MimetypesFileTypeMap;
|
import javax.activation.MimetypesFileTypeMap;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* JBoss, Home of Professional Open Source.
|
* JBoss, Home of Professional Open Source.
|
||||||
+6
-2
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
@@ -13,7 +13,7 @@ public class ObjectUtil {
|
|||||||
* @param str2
|
* @param str2
|
||||||
* @return true if both strings are null or equal
|
* @return true if both strings are null or equal
|
||||||
*/
|
*/
|
||||||
public static boolean isEqualOrNull(Object str1, Object str2) {
|
public static boolean isEqualOrBothNull(Object str1, Object str2) {
|
||||||
if (str1 == null && str2 == null) {
|
if (str1 == null && str2 == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -24,4 +24,8 @@ public class ObjectUtil {
|
|||||||
|
|
||||||
return str1.equals(str2);
|
return str1.equals(str2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String capitalize(String str) {
|
||||||
|
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
+1
-3
@@ -1,8 +1,6 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
|
||||||
import net.iharder.Base64;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
+1
-1
@@ -19,7 +19,7 @@
|
|||||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util.reflections;
|
package org.keycloak.common.util.reflections;
|
||||||
|
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util.reflections;
|
package org.keycloak.common.util.reflections;
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package org.keycloak.util.reflections;
|
package org.keycloak.common.util.reflections;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
name=${product.name}
|
||||||
|
name-html=${product.name-html}
|
||||||
|
version=${product.version}
|
||||||
|
build-time=${product.build-time}
|
||||||
-86
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
|
||||||
* as indicated by the @author tags. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.connections.file;
|
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the InMemoryModel and notifies the factory to save it when
|
|
||||||
* the session is done.
|
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public class DefaultFileConnectionProvider implements FileConnectionProvider {
|
|
||||||
|
|
||||||
private final DefaultFileConnectionProviderFactory factory;
|
|
||||||
private final KeycloakSession session;
|
|
||||||
private final InMemoryModel inMemoryModel;
|
|
||||||
|
|
||||||
private boolean isRollbackOnly = false;
|
|
||||||
|
|
||||||
public DefaultFileConnectionProvider(DefaultFileConnectionProviderFactory factory,
|
|
||||||
KeycloakSession session,
|
|
||||||
InMemoryModel inMemoryModel) {
|
|
||||||
this.factory = factory;
|
|
||||||
this.session = session;
|
|
||||||
this.inMemoryModel = inMemoryModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InMemoryModel getModel() {
|
|
||||||
return inMemoryModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sessionClosed(KeycloakSession session) {
|
|
||||||
factory.sessionClosed(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void begin() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void commit() {
|
|
||||||
factory.commit(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rollback() {
|
|
||||||
factory.rollback(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setRollbackOnly() {
|
|
||||||
isRollbackOnly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getRollbackOnly() {
|
|
||||||
return isRollbackOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActive() {
|
|
||||||
return factory.isActive(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-210
@@ -1,210 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
|
||||||
* as indicated by the @author tags. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.connections.file;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonToken;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.exportimport.Strategy;
|
|
||||||
import org.keycloak.exportimport.util.ExportUtils;
|
|
||||||
import org.keycloak.exportimport.util.ImportUtils;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class dispenses a FileConnectionProvider to Keycloak sessions. It
|
|
||||||
* makes sure that only one InMemoryModel is provided for each session and it
|
|
||||||
* handles thread contention for the file where the model is read or saved.
|
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public class DefaultFileConnectionProviderFactory implements FileConnectionProviderFactory {
|
|
||||||
|
|
||||||
protected static final Logger logger = Logger.getLogger(DefaultFileConnectionProviderFactory.class);
|
|
||||||
|
|
||||||
private File kcdata;
|
|
||||||
private final Map<KeycloakSession, FileConnectionProvider> allProviders = new HashMap<KeycloakSession, FileConnectionProvider>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Config.Scope config) {
|
|
||||||
String fileName = config.get("fileName");
|
|
||||||
if (fileName == null) {
|
|
||||||
fileName = "keycloak-model.json";
|
|
||||||
}
|
|
||||||
|
|
||||||
String directory = config.get("directory");
|
|
||||||
if (directory == null) {
|
|
||||||
directory = System.getProperty("jboss.server.data.dir");
|
|
||||||
}
|
|
||||||
if (directory == null) {
|
|
||||||
directory = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
kcdata = new File(directory, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sessionClosed(KeycloakSession session) {
|
|
||||||
synchronized(allProviders) {
|
|
||||||
allProviders.remove(session);
|
|
||||||
//logger.info("Removed session " + session.hashCode());
|
|
||||||
//logger.info("sessionClosed: Session count=" + allModels.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void readModelFile(KeycloakSession session) {
|
|
||||||
synchronized(allProviders) {
|
|
||||||
if (!kcdata.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInputStream fis = null;
|
|
||||||
try {
|
|
||||||
fis = new FileInputStream(kcdata);
|
|
||||||
Model model = JsonSerialization.readValue(fis, Model.class);
|
|
||||||
ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING);
|
|
||||||
session.realms().getMigrationModel().setStoredVersion(model.getModelVersion());
|
|
||||||
|
|
||||||
ImportUtils.importRealms(session, model.getRealms(), Strategy.IGNORE_EXISTING);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
|
|
||||||
} finally {
|
|
||||||
//logger.info("Read model file for session=" + session.hashCode());
|
|
||||||
try {
|
|
||||||
if (fis != null) {
|
|
||||||
fis.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Failed to close output stream.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeModelFile(KeycloakSession session) {
|
|
||||||
synchronized(allProviders) {
|
|
||||||
FileOutputStream outStream = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
outStream = new FileOutputStream(kcdata);
|
|
||||||
exportModel(session, outStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Unable to write model file " + kcdata.getAbsolutePath(), e);
|
|
||||||
} finally {
|
|
||||||
//logger.info("Wrote model file for session=" + session.hashCode());
|
|
||||||
try {
|
|
||||||
if (outStream != null) {
|
|
||||||
outStream.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("Failed to close output stream.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void exportModel(KeycloakSession session, FileOutputStream outStream) throws IOException {
|
|
||||||
List<RealmModel> realms = session.realms().getRealms();
|
|
||||||
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
|
|
||||||
for (RealmModel realm : realms) {
|
|
||||||
reps.add(ExportUtils.exportRealm(session, realm, true));
|
|
||||||
}
|
|
||||||
Model model = new Model();
|
|
||||||
model.setRealms(reps);
|
|
||||||
model.setModelVersion(session.realms().getMigrationModel().getStoredVersion());
|
|
||||||
JsonSerialization.prettyMapper.writeValue(outStream, model);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileConnectionProvider create(KeycloakSession session) {
|
|
||||||
synchronized (allProviders) {
|
|
||||||
FileConnectionProvider fcProvider = allProviders.get(session);
|
|
||||||
if (fcProvider == null) {
|
|
||||||
InMemoryModel model = new InMemoryModel();
|
|
||||||
fcProvider = new DefaultFileConnectionProvider(this, session, model);
|
|
||||||
allProviders.put(session, fcProvider);
|
|
||||||
session.getTransaction().enlist(fcProvider);
|
|
||||||
readModelFile(session);
|
|
||||||
//logger.info("Added session " + session.hashCode() + " total sessions=" + allModels.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
return fcProvider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// commitCount is used for debugging. This allows you to easily run a test
|
|
||||||
// to a particular point and then examine the JSON file.
|
|
||||||
//private static int commitCount = 0;
|
|
||||||
void commit(KeycloakSession session) {
|
|
||||||
//commitCount++;
|
|
||||||
synchronized (allProviders) {
|
|
||||||
// in case commit was somehow called twice on the same session
|
|
||||||
if (!allProviders.containsKey(session)) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
writeModelFile(session);
|
|
||||||
} finally {
|
|
||||||
allProviders.remove(session);
|
|
||||||
//logger.info("Removed session " + session.hashCode());
|
|
||||||
//logger.info("*** commitCount=" + commitCount);
|
|
||||||
//logger.info("commit(): Session count=" + allModels.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (commitCount == 16) {Thread.dumpStack();System.exit(0);}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rollback(KeycloakSession session) {
|
|
||||||
synchronized (allProviders) {
|
|
||||||
allProviders.remove(session);
|
|
||||||
//logger.info("rollback(): Session count=" + allModels.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isActive(KeycloakSession session) {
|
|
||||||
synchronized (allProviders) {
|
|
||||||
return allProviders.containsKey(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "default";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
|
||||||
* as indicated by the @author tags. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.connections.file;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides an in-memory copy of the entire model for each
|
|
||||||
* Keycloak session. At the start of the session, the model is read
|
|
||||||
* from JSON. When the session's transaction ends, the model is written back
|
|
||||||
* out.
|
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
public class InMemoryModel {
|
|
||||||
private final Map<String, RealmModel> allRealms = new HashMap<String, RealmModel>();
|
|
||||||
|
|
||||||
// realmId, userId, userModel
|
|
||||||
private final Map<String, Map<String,UserModel>> allUsers = new HashMap<String, Map<String,UserModel>>();
|
|
||||||
|
|
||||||
private String modelVersion;
|
|
||||||
|
|
||||||
public InMemoryModel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putRealm(String id, RealmModel realm) {
|
|
||||||
allRealms.put(id, realm);
|
|
||||||
allUsers.put(id, new HashMap<String, UserModel>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getModelVersion() {
|
|
||||||
return modelVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModelVersion(String modelVersion) {
|
|
||||||
this.modelVersion = modelVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmModel getRealm(String id) {
|
|
||||||
return allRealms.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<RealmModel> getRealms() {
|
|
||||||
return allRealms.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmModel getRealmByName(String name) {
|
|
||||||
for (RealmModel realm : getRealms()) {
|
|
||||||
if (realm.getName().equals(name)) return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeRealm(String id) {
|
|
||||||
allUsers.remove(id);
|
|
||||||
return (allRealms.remove(id) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, UserModel> realmUsers(String realmId) {
|
|
||||||
Map<String, UserModel> realmUsers = allUsers.get(realmId);
|
|
||||||
if (realmUsers == null) throw new NullPointerException("Realm users not found for id=" + realmId);
|
|
||||||
return realmUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putUser(String realmId, String userId, UserModel user) {
|
|
||||||
realmUsers(realmId).put(userId, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserModel getUser(String realmId, String userId) {
|
|
||||||
return realmUsers(realmId).get(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasUserWithUsername(String realmId, String username) {
|
|
||||||
for (UserModel user : getUsers(realmId)) {
|
|
||||||
if (user.getUsername().equals(username)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<UserModel> getUsers(String realmId) {
|
|
||||||
return realmUsers(realmId).values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeUser(String realmId, String userId) {
|
|
||||||
return (realmUsers(realmId).remove(userId) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package org.keycloak.connections.file;
|
|
||||||
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class Model {
|
|
||||||
private String modelVersion;
|
|
||||||
private List<RealmRepresentation> realms;
|
|
||||||
|
|
||||||
public String getModelVersion() {
|
|
||||||
return modelVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModelVersion(String modelVersion) {
|
|
||||||
this.modelVersion = modelVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RealmRepresentation> getRealms() {
|
|
||||||
return realms;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealms(List<RealmRepresentation> realms) {
|
|
||||||
this.realms = realms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
org.keycloak.connections.file.DefaultFileConnectionProviderFactory
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
org.keycloak.connections.file.FileConnectionSpi
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-model-api</artifactId>
|
<artifactId>keycloak-model-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-connections-truststore</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
|
|||||||
+62
-40
@@ -10,10 +10,11 @@ import org.apache.http.entity.ContentType;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.connections.truststore.TruststoreProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.util.EnvUtil;
|
import org.keycloak.common.util.EnvUtil;
|
||||||
import org.keycloak.util.KeystoreUtil;
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -28,9 +29,12 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
|
|||||||
private static final Logger logger = Logger.getLogger(DefaultHttpClientFactory.class);
|
private static final Logger logger = Logger.getLogger(DefaultHttpClientFactory.class);
|
||||||
|
|
||||||
private volatile CloseableHttpClient httpClient;
|
private volatile CloseableHttpClient httpClient;
|
||||||
|
private Config.Scope config;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpClientProvider create(KeycloakSession session) {
|
public HttpClientProvider create(KeycloakSession session) {
|
||||||
|
lazyInit(session);
|
||||||
|
|
||||||
return new HttpClientProvider() {
|
return new HttpClientProvider() {
|
||||||
@Override
|
@Override
|
||||||
public HttpClient getHttpClient() {
|
public HttpClient getHttpClient() {
|
||||||
@@ -74,7 +78,9 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
|
|||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
try {
|
try {
|
||||||
httpClient.close();
|
if (httpClient != null) {
|
||||||
|
httpClient.close();
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -87,46 +93,62 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
long socketTimeout = config.getLong("socket-timeout-millis", -1L);
|
this.config = config;
|
||||||
long establishConnectionTimeout = config.getLong("establish-connection-timeout-millis", -1L);
|
}
|
||||||
int maxPooledPerRoute = config.getInt("max-pooled-per-route", 0);
|
|
||||||
int connectionPoolSize = config.getInt("connection-pool-size", 200);
|
|
||||||
boolean disableTrustManager = config.getBoolean("disable-trust-manager", false);
|
|
||||||
boolean disableCookies = config.getBoolean("disable-cookies", true);
|
|
||||||
String hostnameVerificationPolicy = config.get("hostname-verification-policy", "WILDCARD");
|
|
||||||
HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = HttpClientBuilder.HostnameVerificationPolicy.valueOf(hostnameVerificationPolicy);
|
|
||||||
String truststore = config.get("truststore");
|
|
||||||
String truststorePassword = config.get("truststore-password");
|
|
||||||
String clientKeystore = config.get("client-keystore");
|
|
||||||
String clientKeystorePassword = config.get("client-keystore-password");
|
|
||||||
String clientPrivateKeyPassword = config.get("client-key-password");
|
|
||||||
|
|
||||||
HttpClientBuilder builder = new HttpClientBuilder();
|
private void lazyInit(KeycloakSession session) {
|
||||||
builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
|
if (httpClient == null) {
|
||||||
.establishConnectionTimeout(establishConnectionTimeout, TimeUnit.MILLISECONDS)
|
synchronized(this) {
|
||||||
.maxPooledPerRoute(maxPooledPerRoute)
|
if (httpClient == null) {
|
||||||
.connectionPoolSize(connectionPoolSize)
|
long socketTimeout = config.getLong("socket-timeout-millis", -1L);
|
||||||
.hostnameVerification(hostnamePolicy)
|
long establishConnectionTimeout = config.getLong("establish-connection-timeout-millis", -1L);
|
||||||
.disableCookies(disableCookies);
|
int maxPooledPerRoute = config.getInt("max-pooled-per-route", 0);
|
||||||
if (disableTrustManager) builder.disableTrustManager();
|
int connectionPoolSize = config.getInt("connection-pool-size", 200);
|
||||||
if (truststore != null) {
|
boolean disableCookies = config.getBoolean("disable-cookies", true);
|
||||||
truststore = EnvUtil.replace(truststore);
|
String clientKeystore = config.get("client-keystore");
|
||||||
try {
|
String clientKeystorePassword = config.get("client-keystore-password");
|
||||||
builder.trustStore(KeystoreUtil.loadKeyStore(truststore, truststorePassword));
|
String clientPrivateKeyPassword = config.get("client-key-password");
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to load truststore", e);
|
TruststoreProvider truststoreProvider = session.getProvider(TruststoreProvider.class);
|
||||||
|
boolean disableTrustManager = truststoreProvider == null || truststoreProvider.getTruststore() == null;
|
||||||
|
if (disableTrustManager) {
|
||||||
|
logger.warn("Truststore is disabled");
|
||||||
|
}
|
||||||
|
HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = disableTrustManager ? null
|
||||||
|
: HttpClientBuilder.HostnameVerificationPolicy.valueOf(truststoreProvider.getPolicy().name());
|
||||||
|
|
||||||
|
HttpClientBuilder builder = new HttpClientBuilder();
|
||||||
|
builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.establishConnectionTimeout(establishConnectionTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.maxPooledPerRoute(maxPooledPerRoute)
|
||||||
|
.connectionPoolSize(connectionPoolSize)
|
||||||
|
.disableCookies(disableCookies);
|
||||||
|
|
||||||
|
if (disableTrustManager) {
|
||||||
|
// TODO: is it ok to do away with disabling trust manager?
|
||||||
|
//builder.disableTrustManager();
|
||||||
|
} else {
|
||||||
|
builder.hostnameVerification(hostnamePolicy);
|
||||||
|
try {
|
||||||
|
builder.trustStore(truststoreProvider.getTruststore());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to load truststore", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientKeystore != null) {
|
||||||
|
clientKeystore = EnvUtil.replace(clientKeystore);
|
||||||
|
try {
|
||||||
|
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
|
||||||
|
builder.keyStore(clientCertKeystore, clientPrivateKeyPassword);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to load keystore", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpClient = builder.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clientKeystore != null) {
|
|
||||||
clientKeystore = EnvUtil.replace(clientKeystore);
|
|
||||||
try {
|
|
||||||
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
|
|
||||||
builder.keyStore(clientCertKeystore, clientPrivateKeyPassword);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to load keystore", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httpClient = builder.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -145,7 +145,7 @@ public class HttpClientBuilder {
|
|||||||
* Disable cookie management.
|
* Disable cookie management.
|
||||||
*/
|
*/
|
||||||
public HttpClientBuilder disableCookies(boolean disable) {
|
public HttpClientBuilder disableCookies(boolean disable) {
|
||||||
this.disableTrustManager = disable;
|
this.disableCookies = disable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>keycloak-parent</artifactId>
|
<artifactId>keycloak-parent</artifactId>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<version>1.5.0.Final-SNAPSHOT</version>
|
<version>1.8.0.CR2</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@@ -27,5 +27,6 @@
|
|||||||
<artifactId>infinispan-core</artifactId>
|
<artifactId>infinispan-core</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
+1
@@ -126,6 +126,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
|
|||||||
|
|
||||||
Configuration sessionCacheConfiguration = sessionConfigBuilder.build();
|
Configuration sessionCacheConfiguration = sessionConfigBuilder.build();
|
||||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration);
|
cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration);
|
||||||
|
cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration);
|
||||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration);
|
cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loaded 100 of 3421 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user