Compare commits
733
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f348bb7d8 | ||
|
|
630597746e | ||
|
|
f98036296c | ||
|
|
bf1f867f23 | ||
|
|
8215a85930 | ||
|
|
10a5686955 | ||
|
|
2e3c7a3acf | ||
|
|
06713da67b | ||
|
|
46fb1b6582 | ||
|
|
1b3f98db1e | ||
|
|
a190cdd8f2 | ||
|
|
8e25369001 | ||
|
|
8d90ad816a | ||
|
|
da9e42cb21 | ||
|
|
cbab3ff0d0 | ||
|
|
210b570c89 | ||
|
|
a2203df6a3 | ||
|
|
6a2b369014 | ||
|
|
fda6a6657f | ||
|
|
04565dad00 | ||
|
|
ac0ae06463 | ||
|
|
a8775f757f | ||
|
|
e4f4ed923d | ||
|
|
c7915fa95d | ||
|
|
652b118634 | ||
|
|
f122ff1471 | ||
|
|
f76b90fab9 | ||
|
|
ccd8beae92 | ||
|
|
33f01009d2 | ||
|
|
a0e3a71109 | ||
|
|
f35ecba801 | ||
|
|
0d7f26bd14 | ||
|
|
2f7bdd2640 | ||
|
|
3125e4ddd2 | ||
|
|
c624cb27da | ||
|
|
06adf9a094 | ||
|
|
94ed55ee35 | ||
|
|
95c8603b69 | ||
|
|
9ad2a8272e | ||
|
|
6dc17da00a | ||
|
|
97aa1c6b2c | ||
|
|
084fed8377 | ||
|
|
68d86e4fa1 | ||
|
|
5203373047 | ||
|
|
86de481ba1 | ||
|
|
5d075a97dd | ||
|
|
e89499f5db | ||
|
|
a1f0208767 | ||
|
|
d1964c20ab | ||
|
|
1e035bf2e5 | ||
|
|
f3b87f9cf9 | ||
|
|
d2ea45e1eb | ||
|
|
58ad6b1a40 | ||
|
|
772ef3cd10 | ||
|
|
204dfd52c8 | ||
|
|
244d3452d0 | ||
|
|
e65f8a7150 | ||
|
|
90d6991b9d | ||
|
|
87ca3f203a | ||
|
|
3f3a20e178 | ||
|
|
ce1a19fdbe | ||
|
|
26ac92a6b1 | ||
|
|
1c7dd0a533 | ||
|
|
ceb29d76b2 | ||
|
|
6efb354cce | ||
|
|
8ae242a6ed | ||
|
|
5f0f92fd15 | ||
|
|
e521fa3554 | ||
|
|
b8f73cf21f | ||
|
|
d068cddbaf | ||
|
|
99422835ab | ||
|
|
68263360c7 | ||
|
|
34f5ddab0f | ||
|
|
0cc4630fc6 | ||
|
|
ee57c79784 | ||
|
|
7e41b8df27 | ||
|
|
79c1d7bafa | ||
|
|
652b2fee86 | ||
|
|
dfb871c26a | ||
|
|
99f74acb94 | ||
|
|
83f7682606 | ||
|
|
c4a07f3098 | ||
|
|
c9451f4f35 | ||
|
|
dff4922783 | ||
|
|
c05b666f3c | ||
|
|
9905024f08 | ||
|
|
b0cb457287 | ||
|
|
d4946c1c08 | ||
|
|
1d745b5cbd | ||
|
|
c99785f266 | ||
|
|
9ecdec3f45 | ||
|
|
866f45bd95 | ||
|
|
320a79dbff | ||
|
|
1e0b6f1aad | ||
|
|
251ad6727a | ||
|
|
1b1f56d183 | ||
|
|
7dc05a45ac | ||
|
|
7257bab289 | ||
|
|
48a76c2d0d | ||
|
|
d78c83b236 | ||
|
|
d9b0415047 | ||
|
|
af2e3de96d | ||
|
|
1c142cb170 | ||
|
|
d5dae7d444 | ||
|
|
85816be967 | ||
|
|
e66d658955 | ||
|
|
1916fded95 | ||
|
|
8cc8f4c49c | ||
|
|
1a3bf16729 | ||
|
|
5304b533fc | ||
|
|
0156d47d97 | ||
|
|
b04ac6cdba | ||
|
|
f43e4200da | ||
|
|
2ba7aef2b1 | ||
|
|
7a1932cab4 | ||
|
|
59f189229c | ||
|
|
29af7a1d29 | ||
|
|
3d9ddc53e6 | ||
|
|
1a34feda65 | ||
|
|
d8c1081578 | ||
|
|
1085a1da62 | ||
|
|
576db8e0e1 | ||
|
|
fe9dc4a28d | ||
|
|
d2aaae2340 | ||
|
|
84e40cbc2c | ||
|
|
42672a9ae2 | ||
|
|
2dd2173553 | ||
|
|
fcd5400cda | ||
|
|
23b69bb7a7 | ||
|
|
c31b0945d8 | ||
|
|
c71a4ac4e8 | ||
|
|
38c7ca64cb | ||
|
|
bc187fe7b3 | ||
|
|
548c0db0ae | ||
|
|
3ecd1307b0 | ||
|
|
b51661b790 | ||
|
|
02d6e41ce3 | ||
|
|
f884aed25e | ||
|
|
f2dee1a470 | ||
|
|
d5d806760f | ||
|
|
efe2a8f789 | ||
|
|
69bbed999f | ||
|
|
5b7d4184f0 | ||
|
|
422b7ed5ef | ||
|
|
66a2b916f2 | ||
|
|
27ac253098 | ||
|
|
1839b24b90 | ||
|
|
a60ebb6210 | ||
|
|
4dba4f3c02 | ||
|
|
6727af2f35 | ||
|
|
5bcf334bc5 | ||
|
|
ef49bd685e | ||
|
|
092562fb94 | ||
|
|
44e2983803 | ||
|
|
2e848da1a8 | ||
|
|
22ae4ecb23 | ||
|
|
1642ac2394 | ||
|
|
be8aa93cca | ||
|
|
fc7ba85639 | ||
|
|
11d1dd6419 | ||
|
|
e8bd7270e9 | ||
|
|
99e2afa81b | ||
|
|
676792d07d | ||
|
|
0bec2bcd1e | ||
|
|
25328048aa | ||
|
|
e51d2137cf | ||
|
|
5695677873 | ||
|
|
9d86f582ac | ||
|
|
97b0558f71 | ||
|
|
e825be1c79 | ||
|
|
88252c3dfc | ||
|
|
02997b593f | ||
|
|
9de81a0b14 | ||
|
|
ab13182091 | ||
|
|
e75e4580ae | ||
|
|
b7f031d199 | ||
|
|
808d27be2d | ||
|
|
dcf8751ff7 | ||
|
|
ec6e3d80ee | ||
|
|
5a9badf683 | ||
|
|
dd11dcef93 | ||
|
|
e9851957f0 | ||
|
|
57cfbb3770 | ||
|
|
da2590819d | ||
|
|
d74d93a522 | ||
|
|
b3eadcc990 | ||
|
|
f4d4405c20 | ||
|
|
1ebec0e2e5 | ||
|
|
4433347ff3 | ||
|
|
08c539ba51 | ||
|
|
4e538dd138 | ||
|
|
66373ccab2 | ||
|
|
9c82c0700d | ||
|
|
a480e50c16 | ||
|
|
4391bf4f70 | ||
|
|
18f46cb527 | ||
|
|
32b89adc04 | ||
|
|
5a213fcad9 | ||
|
|
d6f1d1bb3c | ||
|
|
c8c0d0e804 | ||
|
|
aeddf2a860 | ||
|
|
b876a5f7bd | ||
|
|
af8949c291 | ||
|
|
e3da38df7c | ||
|
|
a16079e38d | ||
|
|
e13b0fac63 | ||
|
|
26190350c4 | ||
|
|
7598cc62d3 | ||
|
|
b3211fa8c2 | ||
|
|
8dcc909b6e | ||
|
|
fafaf2c490 | ||
|
|
11e2f19503 | ||
|
|
d67bd0eff2 | ||
|
|
43bbea9a7c | ||
|
|
dc366c53a7 | ||
|
|
fe1ede26be | ||
|
|
aca799b28c | ||
|
|
1017a0af1d | ||
|
|
ff25484a34 | ||
|
|
af922920ab | ||
|
|
919a3f1b91 | ||
|
|
1ec8e63e9e | ||
|
|
96c76a059a | ||
|
|
608701aea7 | ||
|
|
142d44a01f | ||
|
|
83c0f537ce | ||
|
|
aeab25b7fe | ||
|
|
ef255c6a5c | ||
|
|
474ebed0ee | ||
|
|
6e55604dc3 | ||
|
|
2dfdfefb1a | ||
|
|
48f78cf80a | ||
|
|
69cc28dd4b | ||
|
|
adb8bcf791 | ||
|
|
bd22d30674 | ||
|
|
afbf1cc33f | ||
|
|
485df59c5b | ||
|
|
43308b326d | ||
|
|
6c9708c700 | ||
|
|
4a962aa75a | ||
|
|
e786158071 | ||
|
|
ea5a767948 | ||
|
|
bde65a6c57 | ||
|
|
94c0a436b5 | ||
|
|
bddf1adeba | ||
|
|
9406a577c2 | ||
|
|
40344726e1 | ||
|
|
08ffea23b7 | ||
|
|
f1ddaa8051 | ||
|
|
7ae62c1060 | ||
|
|
7c51046d8a | ||
|
|
df6d75e2c5 | ||
|
|
3c88d9666d | ||
|
|
ee74025f38 | ||
|
|
d49c7d47a2 | ||
|
|
8f18e9bbbc | ||
|
|
be96a3785b | ||
|
|
f3aeb5ec9f | ||
|
|
71ea61e7a6 | ||
|
|
c6c73e5e59 | ||
|
|
ae242f8480 | ||
|
|
351a17f68d | ||
|
|
1c4f7cb339 | ||
|
|
2713af189f | ||
|
|
be62d1a915 | ||
|
|
68d71a6767 | ||
|
|
46c88ad1bf | ||
|
|
917151123f | ||
|
|
b9e9d6a787 | ||
|
|
db20077c37 | ||
|
|
ec3d027f90 | ||
|
|
50678085c9 | ||
|
|
5af4785ba1 | ||
|
|
f59d42d5d3 | ||
|
|
fc7ae1ee96 | ||
|
|
2699bb9a44 | ||
|
|
212f5b4082 | ||
|
|
9591f48077 | ||
|
|
0a8494a0f2 | ||
|
|
39f2a4ab71 | ||
|
|
39aa09ca36 | ||
|
|
45c5a2ae78 | ||
|
|
a1c612f833 | ||
|
|
bc39bb32d4 | ||
|
|
884308ecb5 | ||
|
|
2d7f232c3d | ||
|
|
afa65d9ead | ||
|
|
605c88a029 | ||
|
|
09994d1730 | ||
|
|
bb9c99ebf6 | ||
|
|
d4819886a6 | ||
|
|
23445123a2 | ||
|
|
773bb43b41 | ||
|
|
493dddde02 | ||
|
|
2e5704c6b4 | ||
|
|
be6c843bba | ||
|
|
064d677fdc | ||
|
|
084ab782da | ||
|
|
1a10a1ef75 | ||
|
|
acc77e4e6c | ||
|
|
2c0ff9be60 | ||
|
|
b9f5946cf0 | ||
|
|
c403968fa8 | ||
|
|
14baef425e | ||
|
|
8da532da93 | ||
|
|
facf701897 | ||
|
|
f9fe9ea5d6 | ||
|
|
f5a80c3007 | ||
|
|
7cff37e707 | ||
|
|
4e8c6b3fc4 | ||
|
|
82282c1c75 | ||
|
|
d7379f29da | ||
|
|
000159226d | ||
|
|
4ebdb8d24b | ||
|
|
fc2df08e15 | ||
|
|
aaa83dc19a | ||
|
|
adff0d5da0 | ||
|
|
285d6931bc | ||
|
|
114b687c5c | ||
|
|
a26ade3988 | ||
|
|
94e2983310 | ||
|
|
fb2f5f6756 | ||
|
|
d7b818cba2 | ||
|
|
8d885d14ed | ||
|
|
39e0023a5f | ||
|
|
84faac0cd6 | ||
|
|
1bce639d35 | ||
|
|
f2061610b9 | ||
|
|
8716f9b41a | ||
|
|
a24c817bb7 | ||
|
|
47997e0056 | ||
|
|
70fd7bea99 | ||
|
|
52565375b3 | ||
|
|
2c327d9eee | ||
|
|
caeb710444 | ||
|
|
0b0b101184 | ||
|
|
f5b4a0788e | ||
|
|
1aa338fe3e | ||
|
|
5615d8d870 | ||
|
|
9711ccd914 | ||
|
|
fbc2b845ad | ||
|
|
39ecca0989 | ||
|
|
f19b6afd85 | ||
|
|
dca347398d | ||
|
|
de6341bd87 | ||
|
|
5cb9dbb87a | ||
|
|
dddc5181e7 | ||
|
|
3b78fa2d5d | ||
|
|
2a64f5e3ce | ||
|
|
8f5b53e3ce | ||
|
|
605f5decb3 | ||
|
|
d00e128920 | ||
|
|
226c1c53c0 | ||
|
|
001b874903 | ||
|
|
cc523ea356 | ||
|
|
5dc2b7b1d5 | ||
|
|
cd84e78b27 | ||
|
|
dedaa24972 | ||
|
|
1b0c4e5d1f | ||
|
|
b7e3fc5f73 | ||
|
|
9b9ec09788 | ||
|
|
bd7ceb0b73 | ||
|
|
9638c0dd83 | ||
|
|
60f40544bb | ||
|
|
066fb7178d | ||
|
|
cd762e5e8c | ||
|
|
f990d68cee | ||
|
|
88f14b39a7 | ||
|
|
eda64584d7 | ||
|
|
2f2a2dced6 | ||
|
|
38a2ea2fdf | ||
|
|
43bad9a608 | ||
|
|
be5f3086ce | ||
|
|
663f12d5e6 | ||
|
|
6e758d721d | ||
|
|
4c98b04ab7 | ||
|
|
f846645989 | ||
|
|
6bc79b590c | ||
|
|
2ad7e870d8 | ||
|
|
87636669ae | ||
|
|
8d627f1329 | ||
|
|
c51cc4703b | ||
|
|
a1124c9e1f | ||
|
|
20e245cb05 | ||
|
|
5c8e165080 | ||
|
|
afe088a9e8 | ||
|
|
162f31baa2 | ||
|
|
28a50df746 | ||
|
|
5a0d600322 | ||
|
|
38c1945ce4 | ||
|
|
939527bd98 | ||
|
|
cc2de52a1a | ||
|
|
4ccc37abbb | ||
|
|
47d1681e90 | ||
|
|
c89a412661 | ||
|
|
5bd53804f9 | ||
|
|
0d957311b4 | ||
|
|
c2495acca9 | ||
|
|
c297b29a13 | ||
|
|
112ebf77d5 | ||
|
|
5993d40fab | ||
|
|
2d82d15e5b | ||
|
|
656b157103 | ||
|
|
392fa21f1e | ||
|
|
fa99b5415d | ||
|
|
514417ac2d | ||
|
|
938cfe2555 | ||
|
|
d6e64a2c5e | ||
|
|
c52d16da6d | ||
|
|
3f62cd9271 | ||
|
|
df7cb4a9d0 | ||
|
|
9a37696d29 | ||
|
|
20fe7ec7b8 | ||
|
|
37cef49565 | ||
|
|
b5ee395aac | ||
|
|
7b89b80f71 | ||
|
|
08f2b0dea9 | ||
|
|
92e17f4b40 | ||
|
|
b8aedd5a3a | ||
|
|
d3e178d459 | ||
|
|
996d8e8cd3 | ||
|
|
0e1a059d8d | ||
|
|
30359b51fa | ||
|
|
d8880e5ff3 | ||
|
|
6819b8e3a5 | ||
|
|
03b521926a | ||
|
|
abfec23404 | ||
|
|
3dd282e11b | ||
|
|
92efe8441e | ||
|
|
95349e6e2e | ||
|
|
15f8e83d4a | ||
|
|
0095968a73 | ||
|
|
ad01d64803 | ||
|
|
30405804bc | ||
|
|
6093d00fdd | ||
|
|
1ba5ea411b | ||
|
|
9dd7a947e9 | ||
|
|
c51e6a916c | ||
|
|
e9b9da6d20 | ||
|
|
557edbec6d | ||
|
|
93650c442a | ||
|
|
d3e9b29d78 | ||
|
|
7cf1b002d2 | ||
|
|
e96ba202e9 | ||
|
|
9a5ca4d367 | ||
|
|
e6928c6456 | ||
|
|
245edcb5b9 | ||
|
|
e4cb97aa49 | ||
|
|
bc93066fe3 | ||
|
|
bf686bd791 | ||
|
|
01e232158b | ||
|
|
c6e0195a3c | ||
|
|
966d939c12 | ||
|
|
c61c05be51 | ||
|
|
7b099e3d6b | ||
|
|
6d257a0d4c | ||
|
|
b80a4a0aa9 | ||
|
|
aabb3428a8 | ||
|
|
80ff7b92db | ||
|
|
7badd3d5e5 | ||
|
|
e977a363ef | ||
|
|
91283878ac | ||
|
|
74ace4006c | ||
|
|
7be957f4f9 | ||
|
|
3643e76a06 | ||
|
|
07bc756785 | ||
|
|
0af68d28f1 | ||
|
|
75e1f50faf | ||
|
|
dcc40b0a63 | ||
|
|
dc0d9a8dce | ||
|
|
021b01f0bd | ||
|
|
b578122f19 | ||
|
|
e014dcce76 | ||
|
|
a1bdf2837e | ||
|
|
0092d9f74d | ||
|
|
e48aafd588 | ||
|
|
d958d85290 | ||
|
|
5b40031251 | ||
|
|
399a4a0511 | ||
|
|
bebe51c885 | ||
|
|
6812514683 | ||
|
|
62de481329 | ||
|
|
c3eb6df220 | ||
|
|
d875b9885d | ||
|
|
9ab023cc6c | ||
|
|
3510e17dd3 | ||
|
|
f50570e350 | ||
|
|
a52cdae1f7 | ||
|
|
826c60ae36 | ||
|
|
d2c9d1569c | ||
|
|
15dca61c59 | ||
|
|
4224496611 | ||
|
|
7467fbaa12 | ||
|
|
580e4fb0b2 | ||
|
|
90b15a070c | ||
|
|
b54061fc3f | ||
|
|
ce7de3b576 | ||
|
|
31d0365e85 | ||
|
|
084da8a03d | ||
|
|
e395ecb749 | ||
|
|
a34cb92fc1 | ||
|
|
c12fe28b2d | ||
|
|
0ac000adca | ||
|
|
1dd0b563b6 | ||
|
|
26acf6fa83 | ||
|
|
dcc19a39d7 | ||
|
|
496062ef0c | ||
|
|
b27f18c380 | ||
|
|
442d65bb5f | ||
|
|
35e4a5e56d | ||
|
|
fb60ca115f | ||
|
|
3d3871b0e6 | ||
|
|
060a59f982 | ||
|
|
c6c0abbedf | ||
|
|
df182c3301 | ||
|
|
56c858d917 | ||
|
|
00068c460a | ||
|
|
5d0b6400df | ||
|
|
413295e15e | ||
|
|
1c3d165e5d | ||
|
|
5fe0406ec9 | ||
|
|
17affe576f | ||
|
|
b86d091ad1 | ||
|
|
ea544c639e | ||
|
|
492e6cd856 | ||
|
|
5680be743c | ||
|
|
1fd22ee672 | ||
|
|
c3faea0428 | ||
|
|
483ac69c47 | ||
|
|
805ad9f250 | ||
|
|
43799fd315 | ||
|
|
91ec4cb5c7 | ||
|
|
634ce85f17 | ||
|
|
b6c52e8859 | ||
|
|
d042f6e2c9 | ||
|
|
cfe8c2956f | ||
|
|
235a8eab02 | ||
|
|
6baefa7490 | ||
|
|
319b29aa7d | ||
|
|
02428e4f0f | ||
|
|
3fe93eebb6 | ||
|
|
f5c7c14ce0 | ||
|
|
f57ab78916 | ||
|
|
087dab6f3d | ||
|
|
3df504b6b4 | ||
|
|
ea98b26766 | ||
|
|
6d6e598ce5 | ||
|
|
67ad7463f4 | ||
|
|
df6b481112 | ||
|
|
5301b556f0 | ||
|
|
2ff7345b08 | ||
|
|
bafcd3f036 | ||
|
|
5f787028df | ||
|
|
e1e65af8ea | ||
|
|
5bb71d396a | ||
|
|
058f44743c | ||
|
|
2077a2a3af | ||
|
|
5a3a0ae437 | ||
|
|
b4204fc806 | ||
|
|
e8c90c1327 | ||
|
|
136f2c8bb7 | ||
|
|
2b5e0e238d | ||
|
|
a6eface8bc | ||
|
|
7659b1aa18 | ||
|
|
a953b52eac | ||
|
|
6f164f0ee9 | ||
|
|
f61a9d7a70 | ||
|
|
4f270bffd0 | ||
|
|
fa90602aa0 | ||
|
|
eaf5034ec7 | ||
|
|
e83de896c9 | ||
|
|
bcd607a1ee | ||
|
|
e1adceeb85 | ||
|
|
106bdc3c52 | ||
|
|
5229807020 | ||
|
|
1e89467b31 | ||
|
|
f65b502a96 | ||
|
|
efe8342965 | ||
|
|
90cb3acd2f | ||
|
|
98dd87efbc | ||
|
|
ce6576ce84 | ||
|
|
5d4869f915 | ||
|
|
92567a070c | ||
|
|
30955c4e2c | ||
|
|
f9d51db182 | ||
|
|
852e799fea | ||
|
|
bbc3e99bb5 | ||
|
|
e8e22ee44c | ||
|
|
7a211beede | ||
|
|
24279b138b | ||
|
|
a2718a889d | ||
|
|
756de9f7f6 | ||
|
|
143d176dcd | ||
|
|
a1f7cfaf3a | ||
|
|
a549b3ac70 | ||
|
|
43c9179204 | ||
|
|
dfe232cf80 | ||
|
|
ba88cdce70 | ||
|
|
269f9fe860 | ||
|
|
4a670ab236 | ||
|
|
ab934f3d35 | ||
|
|
24eca809f1 | ||
|
|
57780b6520 | ||
|
|
f5e301fe0d | ||
|
|
68976f5b0c | ||
|
|
8e7e208265 | ||
|
|
0951c11aa6 | ||
|
|
3d4ca01242 | ||
|
|
319f86e91a | ||
|
|
490b3e3603 | ||
|
|
7476ee51e4 | ||
|
|
a9f1fda68a | ||
|
|
975337f225 | ||
|
|
1490f106f2 | ||
|
|
61c35265a6 | ||
|
|
0f4497e53e | ||
|
|
ca60c283fc | ||
|
|
37298bdbd6 | ||
|
|
d70fd86a82 | ||
|
|
ad606cbce6 | ||
|
|
4e0a32a37a | ||
|
|
7290040008 | ||
|
|
91bf50eb6c | ||
|
|
748ed30b47 | ||
|
|
b88d2f792d | ||
|
|
c8b19fa1ae | ||
|
|
c0795ce2c7 | ||
|
|
12c3aa83dd | ||
|
|
c193ba0c81 | ||
|
|
d8226c457f | ||
|
|
a507771f7c | ||
|
|
2f8d577e95 | ||
|
|
362babe5e2 | ||
|
|
d9297cee16 | ||
|
|
3851a2fe72 | ||
|
|
338300df32 | ||
|
|
0c7f63b2f3 | ||
|
|
9a87b7f196 | ||
|
|
abcd830af1 | ||
|
|
f807c999b6 | ||
|
|
ccd5deb85f | ||
|
|
dbf9525860 | ||
|
|
502efe42c9 | ||
|
|
f0787d98d1 | ||
|
|
d2ed8302a7 | ||
|
|
15310d1e97 | ||
|
|
38af401a16 | ||
|
|
ab1d39dfb3 | ||
|
|
d35786fe49 | ||
|
|
b9db84edf3 | ||
|
|
c3176cac6a | ||
|
|
6c98d64874 | ||
|
|
269dc26029 | ||
|
|
48d26fc0e9 | ||
|
|
797bf49ea6 | ||
|
|
fd96bacf70 | ||
|
|
7f9739cd4f | ||
|
|
698660c92a | ||
|
|
5aa90243a0 | ||
|
|
4d32ac8765 | ||
|
|
224f85b981 | ||
|
|
29c72c0744 | ||
|
|
36c162126b | ||
|
|
252dfe006b | ||
|
|
d1743f0608 | ||
|
|
9168cf811c | ||
|
|
9e50ae6a01 | ||
|
|
371c798116 | ||
|
|
d00e0021b6 | ||
|
|
bce3e61b7d | ||
|
|
ccbd4bace1 | ||
|
|
78999537f0 | ||
|
|
e3b0cc7093 | ||
|
|
50014f4398 | ||
|
|
833c276424 | ||
|
|
770525a385 | ||
|
|
e4204a56f5 | ||
|
|
d37a9eada3 | ||
|
|
a7bfae2f56 | ||
|
|
aaebcd074e | ||
|
|
58fc4520c9 | ||
|
|
53716697ca | ||
|
|
96668daefa | ||
|
|
4b033d3d93 | ||
|
|
8ca9a6a64a | ||
|
|
2ce3925ba9 | ||
|
|
3d9adbe991 | ||
|
|
f16828e129 | ||
|
|
91eb80beb0 | ||
|
|
30682968ff | ||
|
|
620e3b67e9 | ||
|
|
afe1ddd719 | ||
|
|
0aefacc88e | ||
|
|
366c707afa | ||
|
|
707e29adef | ||
|
|
66f2735224 | ||
|
|
86e19c274f | ||
|
|
46bb59e348 | ||
|
|
37cbba57e6 | ||
|
|
b05da425b9 | ||
|
|
c28f5cef7a | ||
|
|
f65e804836 | ||
|
|
01e9b3bcff | ||
|
|
4e58bed39e | ||
|
|
7d2942e1be | ||
|
|
943404689e | ||
|
|
89875aea02 | ||
|
|
70ce96caa0 | ||
|
|
625fb02dfe | ||
|
|
18966334ab | ||
|
|
a8f3756bbc | ||
|
|
f0dab97b80 | ||
|
|
3a7bba041f | ||
|
|
a50c0e61e4 | ||
|
|
87cb28eaab | ||
|
|
03fa58a271 | ||
|
|
666e610334 | ||
|
|
3c76a85674 | ||
|
|
c571ad9902 | ||
|
|
355c440c3d | ||
|
|
9a17658f84 | ||
|
|
69347aa7f6 | ||
|
|
bf259f4c98 | ||
|
|
082ebe8043 | ||
|
|
a13a02c995 | ||
|
|
3a9ac80786 | ||
|
|
d3cf177ebc | ||
|
|
7b49dbb74e | ||
|
|
4cc249bcd4 | ||
|
|
035529d7a2 | ||
|
|
fa91c0ed03 | ||
|
|
adbde56c1a | ||
|
|
70f53c6c06 |
No files matched your search
@@ -0,0 +1,20 @@
|
||||
* text=auto
|
||||
|
||||
*.html text eol=lf
|
||||
*.java text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.jsp text eol=lf
|
||||
*.md text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.svg text auto
|
||||
*.xml text eol=lf
|
||||
*.xsl text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.gif binary
|
||||
*.ttf binary
|
||||
*.eot binary
|
||||
*.otf binary
|
||||
*.woff binary
|
||||
+13
-2
@@ -1,8 +1,19 @@
|
||||
language: java
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
sudo: false
|
||||
|
||||
after_script: 'misc/.travis/after.sh'
|
||||
before_cache:
|
||||
- rm -rf $HOME/.m2/repository/org/keycloak
|
||||
|
||||
install: mvn install -Pdistribution -DskipTests=true -B -V
|
||||
|
||||
script:
|
||||
- mvn test -B
|
||||
- mvn -file testsuite/integration-arquillian test -B
|
||||
|
||||
sudo: false
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -58,6 +58,11 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
return null;
|
||||
@@ -73,6 +78,11 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
|
||||
|
||||
+2
@@ -18,8 +18,10 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
+7
@@ -1,8 +1,10 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -28,4 +30,9 @@ public abstract class AbstractIdentityProviderMapper implements IdentityProvider
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ public class BrokeredIdentityContext {
|
||||
|
||||
private String id;
|
||||
private String username;
|
||||
private String modelUsername;
|
||||
private String email;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
@@ -59,6 +60,11 @@ public class BrokeredIdentityContext {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Username in remote idp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
@@ -67,6 +73,19 @@ public class BrokeredIdentityContext {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* username to store in UserModel
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getModelUsername() {
|
||||
return modelUsername;
|
||||
}
|
||||
|
||||
public void setModelUsername(String modelUsername) {
|
||||
this.modelUsername = modelUsername;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
@@ -27,15 +27,15 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Role to grant to user. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.ROLE_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String[] parseRole(String role) {
|
||||
int scopeIndex = role.indexOf('.');
|
||||
int scopeIndex = role.lastIndexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
String appName = role.substring(0, scopeIndex);
|
||||
role = role.substring(scopeIndex + 1);
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.keycloak.broker.provider;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
@@ -35,7 +36,7 @@ import javax.ws.rs.core.UriInfo;
|
||||
*/
|
||||
public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
|
||||
|
||||
public interface AuthenticationCallback {
|
||||
interface AuthenticationCallback {
|
||||
/**
|
||||
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
|
||||
* with the remote IDP
|
||||
@@ -43,9 +44,15 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public Response authenticated(BrokeredIdentityContext context);
|
||||
Response authenticated(BrokeredIdentityContext context);
|
||||
|
||||
Response cancelled(String code);
|
||||
|
||||
Response error(String code, String message);
|
||||
}
|
||||
|
||||
|
||||
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, BrokeredIdentityContext context);
|
||||
void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
|
||||
Regular → Executable
+2
@@ -18,6 +18,8 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -19,7 +19,37 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
|
||||
String getDisplayCategory();
|
||||
String getDisplayType();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param mapperModel
|
||||
* @param context
|
||||
*/
|
||||
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called after UserModel is created for first time for this user.
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param user
|
||||
* @param mapperModel
|
||||
* @param context
|
||||
*/
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* Called when this user has logged in before and has already been imported.
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param user
|
||||
* @param mapperModel
|
||||
* @param context
|
||||
*/
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class IdentityProviderMapperSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "identity-provider-mapper";
|
||||
|
||||
@@ -28,6 +28,11 @@ public class IdentityProviderSpi implements Spi {
|
||||
|
||||
public static final String IDENTITY_PROVIDER_SPI_NAME = "identity_provider";
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return IDENTITY_PROVIDER_SPI_NAME;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+6
-3
@@ -58,6 +58,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
public static final String FEDERATED_ACCESS_TOKEN = "FEDERATED_ACCESS_TOKEN";
|
||||
public static final String FEDERATED_REFRESH_TOKEN = "FEDERATED_REFRESH_TOKEN";
|
||||
public static final String FEDERATED_TOKEN_EXPIRATION = "FEDERATED_TOKEN_EXPIRATION";
|
||||
public static final String ACCESS_DENIED = "access_denied";
|
||||
protected static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public static final String OAUTH2_PARAMETER_ACCESS_TOKEN = "access_token";
|
||||
@@ -213,9 +214,11 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
@QueryParam(OAuth2Constants.ERROR) String error) {
|
||||
if (error != null) {
|
||||
//logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(error);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
if (error.equals(ACCESS_DENIED)) {
|
||||
return callback.cancelled(state);
|
||||
} else {
|
||||
return callback.error(state, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
+7
-1
@@ -17,10 +17,15 @@
|
||||
*/
|
||||
package org.keycloak.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -50,4 +55,5 @@ public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProvide
|
||||
return OIDCIdentityProviderFactory.parseOIDCConfig(inputStream);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.keycloak.broker.oidc;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.oidc.mappers.AbstractJsonUserAttributeMapper;
|
||||
import org.keycloak.broker.oidc.util.JsonSimpleHttp;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
@@ -50,6 +51,7 @@ import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
@@ -224,7 +226,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
name = getJsonProperty(userInfo, "name");
|
||||
preferredUsername = getJsonProperty(userInfo, "preferred_username");
|
||||
email = getJsonProperty(userInfo, "email");
|
||||
identity.getContextData().put(USER_INFO, userInfo);
|
||||
AbstractJsonUserAttributeMapper.storeUserProfileForMapper(identity, userInfo, getConfig().getAlias());
|
||||
}
|
||||
identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
|
||||
identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
*/
|
||||
package org.keycloak.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.oidc.mappers.UsernameTemplateMapper;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jwk.JWKParser;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
|
||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||
@@ -30,6 +34,7 @@ import org.keycloak.util.JsonSerialization;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -89,7 +94,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("F ailed to query JWKSet from: " + uri, e);
|
||||
throw new RuntimeException("Failed to query JWKSet from: " + uri, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,4 +104,5 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
protected static boolean keyTypeSupported(String type) {
|
||||
return type != null && type.equals("RSA");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,8 +33,12 @@ public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
public static Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String claim = mapperModel.getConfig().get(CLAIM);
|
||||
return getClaimValue(context, claim);
|
||||
}
|
||||
|
||||
public static Object getClaimValue(BrokeredIdentityContext context, String claim) {
|
||||
{ // search access token
|
||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
||||
if (token != null) {
|
||||
|
||||
Executable
+206
@@ -0,0 +1,206 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProvider;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* Abstract class for Social Provider mappers which allow mapping of JSON user profile field into Keycloak user
|
||||
* attribute. Concrete mapper classes with own ID and provider mapping must be implemented for each social provider who
|
||||
* uses {@link JsonNode} user profile.
|
||||
*
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(AbstractJsonUserAttributeMapper.class);
|
||||
|
||||
protected static final Logger LOGGER_DUMP_USER_PROFILE = Logger.getLogger("org.keycloak.social.user_profile_dump");
|
||||
|
||||
private static final String JSON_PATH_DELIMITER = ".";
|
||||
|
||||
/**
|
||||
* Config param where name of mapping source JSON User Profile field is stored.
|
||||
*/
|
||||
public static final String CONF_JSON_FIELD = "jsonField";
|
||||
/**
|
||||
* Config param where name of mapping target USer attribute is stored.
|
||||
*/
|
||||
public static final String CONF_USER_ATTRIBUTE = "userAttribute";
|
||||
|
||||
/**
|
||||
* Key in {@link BrokeredIdentityContext#getContextData()} where {@link JsonNode} with user profile is stored.
|
||||
*/
|
||||
public static final String CONTEXT_JSON_NODE = OIDCIdentityProvider.USER_INFO;
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
ProviderConfigProperty property1;
|
||||
property1 = new ProviderConfigProperty();
|
||||
property1.setName(CONF_JSON_FIELD);
|
||||
property1.setLabel("Social Profile JSON Field Path");
|
||||
property1.setHelpText("Path of field in Social provider User Profile JSON data to get value from. You can use dot notation for nesting and square brackets for array index. Eg. 'contact.address[0].country'.");
|
||||
property1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property1);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(CONF_USER_ATTRIBUTE);
|
||||
property.setLabel("User Attribute Name");
|
||||
property.setHelpText("User attribute name to store information into.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store used profile JsonNode into user context for later use by this mapper. Profile data are dumped into special logger if enabled also to allow investigation of the structure.
|
||||
*
|
||||
* @param user context to store profile data into
|
||||
* @param profile to store into context
|
||||
* @param provider identification of social provider to be used in log dump
|
||||
*
|
||||
* @see #importNewUser(KeycloakSession, RealmModel, UserModel, IdentityProviderMapperModel, BrokeredIdentityContext)
|
||||
* @see BrokeredIdentityContext#getContextData()
|
||||
*/
|
||||
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
|
||||
user.getContextData().put(AbstractJsonUserAttributeMapper.CONTEXT_JSON_NODE, profile);
|
||||
if (LOGGER_DUMP_USER_PROFILE.isDebugEnabled())
|
||||
LOGGER_DUMP_USER_PROFILE.debug("User Profile JSON Data for provider "+provider+": " + profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Import user profile information if it exists in Social provider JSON data into the specified user attribute.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
|
||||
if (attribute == null || attribute.trim().isEmpty()) {
|
||||
logger.debug("Attribute is not configured");
|
||||
return;
|
||||
}
|
||||
attribute = attribute.trim();
|
||||
|
||||
String value = getJsonValue(mapperModel, context);
|
||||
if (value != null) {
|
||||
user.setSingleAttribute(attribute, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
// we do not update user profile from social provider
|
||||
}
|
||||
|
||||
protected static String getJsonValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
|
||||
if (jsonField == null || jsonField.trim().isEmpty()) {
|
||||
logger.debug("JSON field path is not configured");
|
||||
return null;
|
||||
}
|
||||
jsonField = jsonField.trim();
|
||||
|
||||
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
|
||||
logger.debug("JSON field path is invalid " + jsonField);
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonNode profileJsonNode = (JsonNode) context.getContextData().get(CONTEXT_JSON_NODE);
|
||||
|
||||
String value = getJsonValue(profileJsonNode, jsonField);
|
||||
|
||||
if (value == null) {
|
||||
logger.debug("User profile JSON value '" + jsonField + "' is not available.");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
protected static String getJsonValue(JsonNode baseNode, String fieldPath) {
|
||||
logger.debug("Going to process JsonNode path " + fieldPath + " on data " + baseNode);
|
||||
if (baseNode != null) {
|
||||
|
||||
int idx = fieldPath.indexOf(JSON_PATH_DELIMITER);
|
||||
|
||||
String currentFieldName = fieldPath;
|
||||
if (idx > 0) {
|
||||
currentFieldName = fieldPath.substring(0, idx).trim();
|
||||
if (currentFieldName.isEmpty()) {
|
||||
logger.debug("JSON path is invalid " + fieldPath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String currentNodeName = currentFieldName;
|
||||
int arrayIndex = -1;
|
||||
if (currentFieldName.endsWith("]")) {
|
||||
int bi = currentFieldName.indexOf("[");
|
||||
if (bi == -1) {
|
||||
logger.debug("Invalid array index construct in " + currentFieldName);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String is = currentFieldName.substring(bi+1, currentFieldName.length() - 1).trim();
|
||||
arrayIndex = Integer.parseInt(is);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Invalid array index construct in " + currentFieldName);
|
||||
return null;
|
||||
}
|
||||
currentNodeName = currentFieldName.substring(0,bi).trim();
|
||||
}
|
||||
|
||||
JsonNode currentNode = baseNode.get(currentNodeName);
|
||||
if (arrayIndex > -1 && currentNode.isArray()) {
|
||||
logger.debug("Going to take array node at index " + arrayIndex);
|
||||
currentNode = currentNode.get(arrayIndex);
|
||||
}
|
||||
|
||||
if (currentNode == null) {
|
||||
logger.debug("JsonNode not found for name " + currentFieldName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
if (!currentNode.isValueNode()) {
|
||||
logger.debug("JsonNode is not value node for name " + currentFieldName);
|
||||
return null;
|
||||
}
|
||||
String ret = currentNode.asText();
|
||||
if (ret != null && !ret.trim().isEmpty())
|
||||
return ret.trim();
|
||||
} else {
|
||||
return getJsonValue(currentNode, fieldPath.substring(idx + 1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,8 +43,8 @@ public class ClaimToRoleMapper extends AbstractClaimMapper {
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user if claim is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Role to grant to user if claim is present. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.ROLE_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user if external role is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Role to grant to user if external role is present. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.ROLE_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@ package org.keycloak.broker.oidc.mappers;
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
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.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -76,7 +74,7 @@ public class UserAttributeMapper extends AbstractClaimMapper {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getClaimValue(mapperModel, context);
|
||||
if (value != null) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
user.setSingleAttribute(attribute, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +82,9 @@ public class UserAttributeMapper extends AbstractClaimMapper {
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getClaimValue(mapperModel, context);
|
||||
String current = user.getAttribute(attribute);
|
||||
String current = user.getFirstAttribute(attribute);
|
||||
if (value != null && !value.equals(current)) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
user.setSingleAttribute(attribute, value.toString());
|
||||
} else if (value == null) {
|
||||
user.removeAttribute(attribute);
|
||||
}
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UsernameTemplateMapper extends AbstractClaimMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
public static final String TEMPLATE = "template";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(TEMPLATE);
|
||||
property.setLabel("Template");
|
||||
property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${CLAIM.sub}'. ALIAS is the provider alias. CLAIM.<NAME> references an ID or Access token claim.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setDefaultValue("${ALIAS}.${CLAIM.preferred_username}");
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-username-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Preprocessor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Username Template Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
}
|
||||
|
||||
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String template = mapperModel.getConfig().get(TEMPLATE);
|
||||
Matcher m = substitution.matcher(template);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String variable = m.group(1);
|
||||
if (variable.equals("ALIAS")) {
|
||||
m.appendReplacement(sb, context.getIdpConfig().getAlias());
|
||||
} else if (variable.equals("UUID")) {
|
||||
m.appendReplacement(sb, KeycloakModelUtils.generateId());
|
||||
} else if (variable.startsWith("CLAIM.")) {
|
||||
String name = variable.substring("CLAIM.".length());
|
||||
Object value = AbstractClaimMapper.getClaimValue(context, name);
|
||||
if (value == null) value = "";
|
||||
m.appendReplacement(sb, value.toString());
|
||||
} else {
|
||||
m.appendReplacement(sb, m.group(1));
|
||||
}
|
||||
|
||||
}
|
||||
m.appendTail(sb);
|
||||
String username = sb.toString();
|
||||
context.setModelUsername(username);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Format the username to import.";
|
||||
}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
|
||||
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
|
||||
org.keycloak.broker.oidc.mappers.UserAttributeMapper
|
||||
org.keycloak.broker.oidc.mappers.UsernameTemplateMapper
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @authors tag. All rights reserved.
|
||||
*/
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.JsonProcessingException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractJsonUserAttributeMapper}
|
||||
*
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public class AbstractJsonUserAttributeMapperTest {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private static JsonNode baseNode;
|
||||
|
||||
private JsonNode getJsonNode() throws JsonProcessingException, IOException {
|
||||
if (baseNode == null)
|
||||
baseNode = mapper.readTree("{ \"value1\" : \"v1 \",\"value_empty\" : \"\", \"value_b\" : true, \"value_i\" : 454, " + " \"value_array\":[\"a1\",\"a2\"], " +" \"nest1\": {\"value1\": \" fgh \",\"value_empty\" : \"\", \"nest2\":{\"value_b\" : false, \"value_i\" : 43}}, "+ " \"nesta\": { \"a\":[{\"av1\": \"vala1\"},{\"av1\": \"vala2\"}]}"+" }");
|
||||
return baseNode;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonValue_invalidPath() throws JsonProcessingException, IOException {
|
||||
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "."));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".."));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "...value1"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), ".value1"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1."));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[]"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "[value1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonValue_simpleValues() throws JsonProcessingException, IOException {
|
||||
|
||||
//unknown field returns null
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_unknown"));
|
||||
|
||||
// we check value is trimmed also!
|
||||
Assert.assertEquals("v1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_empty"));
|
||||
|
||||
Assert.assertEquals("true", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_b"));
|
||||
Assert.assertEquals("454", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_i"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonValue_nestedSimpleValues() throws JsonProcessingException, IOException {
|
||||
|
||||
// null if path points to JSON object
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2"));
|
||||
|
||||
//unknown field returns null
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_unknown"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_unknown"));
|
||||
|
||||
// we check value is trimmed also!
|
||||
Assert.assertEquals("fgh", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.value_empty"));
|
||||
|
||||
Assert.assertEquals("false", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_b"));
|
||||
Assert.assertEquals("43", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2.value_i"));
|
||||
|
||||
// null if invalid nested path
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1."));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nest1.nest2."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonValue_simpleArray() throws JsonProcessingException, IOException {
|
||||
|
||||
// array field itself returns null if no index is provided
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array"));
|
||||
// outside index returns null
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[2]"));
|
||||
|
||||
//corect index
|
||||
Assert.assertEquals("a1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[0]"));
|
||||
Assert.assertEquals("a2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[1]"));
|
||||
|
||||
//incorrect array constructs
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[]"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array]"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array["));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[a]"));
|
||||
Assert.assertNull(AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "value_array[-2]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonValue_nestedArrayWithObjects() throws JsonProcessingException, IOException {
|
||||
Assert.assertEquals("vala1", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av1"));
|
||||
Assert.assertEquals("vala2", AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[1].av1"));
|
||||
|
||||
//different path erros or nonexisting indexes or fields return null
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[2].av1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0]"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[0].av_unknown"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[].av1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a.av1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a].av1"));
|
||||
Assert.assertEquals(null, AbstractJsonUserAttributeMapper.getJsonValue(getJsonNode(), "nesta.a[.av1"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -180,10 +180,10 @@ public class SAMLEndpoint {
|
||||
SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
|
||||
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
|
||||
// validate destination
|
||||
if (!uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
|
||||
if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) {
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
@@ -215,6 +215,9 @@ public class SAMLEndpoint {
|
||||
if (request.getSessionIndex() == null || request.getSessionIndex().isEmpty()) {
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, brokerUserId);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
|
||||
} catch (Exception e) {
|
||||
@@ -227,6 +230,9 @@ public class SAMLEndpoint {
|
||||
String brokerSessionId = brokerUserId + "." + sessionIndex;
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||
if (userSession != null) {
|
||||
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
|
||||
} catch (Exception e) {
|
||||
@@ -280,7 +286,8 @@ public class SAMLEndpoint {
|
||||
|
||||
identity.setUsername(subjectNameID.getValue());
|
||||
|
||||
if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
|
||||
//SAML Spec 2.2.2 Format is optional
|
||||
if (subjectNameID.getFormat() != null && subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
|
||||
identity.setEmail(subjectNameID.getValue());
|
||||
}
|
||||
|
||||
@@ -348,10 +355,10 @@ public class SAMLEndpoint {
|
||||
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
|
||||
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
|
||||
// validate destination
|
||||
if (!uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
|
||||
if (statusResponse.getDestination() != null && !uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
|
||||
@@ -18,13 +18,17 @@
|
||||
package org.keycloak.broker.saml;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.broker.saml.mappers.UsernameTemplateMapper;
|
||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
@@ -150,4 +154,5 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -59,8 +59,8 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setHelpText("Role to grant to user. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.ROLE_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,14 @@ package org.keycloak.broker.saml.mappers;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
@@ -87,7 +84,7 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getAttribute(mapperModel, context);
|
||||
if (value != null) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
user.setSingleAttribute(attribute, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +112,9 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getAttribute(mapperModel, context);
|
||||
String current = user.getAttribute(attribute);
|
||||
String current = user.getFirstAttribute(attribute);
|
||||
if (value != null && !value.equals(current)) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
user.setSingleAttribute(attribute, value.toString());
|
||||
} else if (value == null) {
|
||||
user.removeAttribute(attribute);
|
||||
}
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package org.keycloak.broker.saml.mappers;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
public static final String TEMPLATE = "template";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(TEMPLATE);
|
||||
property.setLabel("Template");
|
||||
property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${NAMEID}'. ALIAS is the provider alias. NAMEID is that SAML name id assertion. ATTRIBUTE.<NAME> references a SAML attribute where name is the attribute name or friendly name.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
property.setDefaultValue("${ALIAS}.${NAMEID}");
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-username-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Preprocessor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Username Template Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
|
||||
@Override
|
||||
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
String template = mapperModel.getConfig().get(TEMPLATE);
|
||||
Matcher m = substitution.matcher(template);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String variable = m.group(1);
|
||||
if (variable.equals("ALIAS")) {
|
||||
m.appendReplacement(sb, context.getIdpConfig().getAlias());
|
||||
} else if (variable.equals("UUID")) {
|
||||
m.appendReplacement(sb, KeycloakModelUtils.generateId());
|
||||
} else if (variable.equals("NAMEID")) {
|
||||
SubjectType subject = assertion.getSubject();
|
||||
SubjectType.STSubType subType = subject.getSubType();
|
||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||
m.appendReplacement(sb, subjectNameID.getValue());
|
||||
} else if (variable.startsWith("ATTRIBUTE.")) {
|
||||
String name = variable.substring("ATTRIBUTE.".length());
|
||||
String value = "";
|
||||
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
|
||||
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
|
||||
AttributeType attr = choice.getAttribute();
|
||||
if (name.equals(attr.getName()) || name.equals(attr.getFriendlyName())) {
|
||||
List<Object> attributeValue = attr.getAttributeValue();
|
||||
if (attributeValue != null && !attributeValue.isEmpty()) {
|
||||
value = attributeValue.get(0).toString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m.appendReplacement(sb, value);
|
||||
} else {
|
||||
m.appendReplacement(sb, m.group(1));
|
||||
}
|
||||
|
||||
}
|
||||
m.appendTail(sb);
|
||||
context.setModelUsername(sb.toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Format the username to import.";
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
|
||||
org.keycloak.broker.saml.mappers.UserAttributeMapper
|
||||
org.keycloak.broker.saml.mappers.UsernameTemplateMapper
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+1
-12
@@ -90,18 +90,7 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
|
||||
ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING);
|
||||
session.realms().getMigrationModel().setStoredVersion(model.getModelVersion());
|
||||
|
||||
List<RealmRepresentation> realmReps = new ArrayList<RealmRepresentation>();
|
||||
for (RealmRepresentation realmRep : model.getRealms()) {
|
||||
if (Config.getAdminRealm().equals(realmRep.getRealm())) {
|
||||
realmReps.add(0, realmRep);
|
||||
} else {
|
||||
realmReps.add(realmRep);
|
||||
}
|
||||
}
|
||||
for (RealmRepresentation realmRep : realmReps) {
|
||||
ImportUtils.importRealm(session, realmRep, Strategy.IGNORE_EXISTING);
|
||||
}
|
||||
|
||||
ImportUtils.importRealms(session, model.getRealms(), Strategy.IGNORE_EXISTING);
|
||||
} catch (IOException ioe) {
|
||||
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
|
||||
} finally {
|
||||
|
||||
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class FileConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsFile";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+5
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class HttpClientSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsHttpClient";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+5
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class InfinispanConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsInfinispan";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -54,7 +54,7 @@
|
||||
<configuration>
|
||||
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
|
||||
|
||||
<url>${url}</url>
|
||||
<url>${project.url}</url>
|
||||
<driver>${driver}</driver>
|
||||
<username>${username}</username>
|
||||
<password>${password}</password>
|
||||
|
||||
+15
-8
@@ -46,26 +46,26 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
private static final String CHANGELOG = "META-INF/jpa-changelog-master.xml";
|
||||
|
||||
@Override
|
||||
public String getCurrentVersionSql() {
|
||||
return "SELECT ID from DATABASECHANGELOG ORDER BY DATEEXECUTED DESC LIMIT 1";
|
||||
public String getCurrentVersionSql(String defaultSchema) {
|
||||
return "SELECT ID from " + getTable("DATABASECHANGELOG", defaultSchema) + " ORDER BY DATEEXECUTED DESC LIMIT 1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(KeycloakSession session, Connection connection) {
|
||||
public void update(KeycloakSession session, Connection connection, String defaultSchema) {
|
||||
logger.debug("Starting database update");
|
||||
|
||||
// Need ThreadLocal as liquibase doesn't seem to have API to inject custom objects into tasks
|
||||
ThreadLocalSessionContext.setCurrentSession(session);
|
||||
|
||||
try {
|
||||
Liquibase liquibase = getLiquibase(connection);
|
||||
Liquibase liquibase = getLiquibase(connection, defaultSchema);
|
||||
|
||||
List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
|
||||
if (!changeSets.isEmpty()) {
|
||||
if (changeSets.get(0).getId().equals(FIRST_VERSION)) {
|
||||
Statement statement = connection.createStatement();
|
||||
try {
|
||||
statement.executeQuery("SELECT id FROM REALM");
|
||||
statement.executeQuery("SELECT id FROM " + getTable("REALM", defaultSchema));
|
||||
|
||||
logger.infov("Updating database from {0} to {1}", FIRST_VERSION, changeSets.get(changeSets.size() - 1).getId());
|
||||
liquibase.markNextChangeSetRan(null);
|
||||
@@ -93,9 +93,9 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Connection connection) {
|
||||
public void validate(Connection connection, String defaultSchema) {
|
||||
try {
|
||||
Liquibase liquibase = getLiquibase(connection);
|
||||
Liquibase liquibase = getLiquibase(connection, defaultSchema);
|
||||
|
||||
liquibase.validate();
|
||||
} catch (Exception e) {
|
||||
@@ -103,7 +103,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private Liquibase getLiquibase(Connection connection) throws Exception {
|
||||
private Liquibase getLiquibase(Connection connection, String defaultSchema) throws Exception {
|
||||
ServiceLocator sl = ServiceLocator.getInstance();
|
||||
|
||||
if (!System.getProperties().containsKey("liquibase.scan.packages")) {
|
||||
@@ -125,6 +125,9 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
|
||||
LogFactory.setInstance(new LogWrapper());
|
||||
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
|
||||
if (defaultSchema != null) {
|
||||
database.setDefaultSchemaName(defaultSchema);
|
||||
}
|
||||
return new Liquibase(CHANGELOG, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
|
||||
}
|
||||
|
||||
@@ -228,4 +231,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
|
||||
}
|
||||
|
||||
private String getTable(String table, String defaultSchema) {
|
||||
return defaultSchema != null ? defaultSchema + "." + table : table;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -3,6 +3,7 @@ package org.keycloak.connections.jpa.updater.liquibase.custom;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import liquibase.datatype.DataTypeFactory;
|
||||
import liquibase.exception.CustomChangeException;
|
||||
import liquibase.statement.core.InsertStatement;
|
||||
import liquibase.structure.core.Table;
|
||||
@@ -17,7 +18,9 @@ public class JpaUpdate1_2_0_CR1 extends CustomKeycloakTask {
|
||||
String realmClientTableName = database.correctObjectName("REALM_CLIENT", Table.class);
|
||||
|
||||
try {
|
||||
PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = true");
|
||||
String trueValue = DataTypeFactory.getInstance().getTrueBooleanValue(database);
|
||||
PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = " + trueValue);
|
||||
|
||||
try {
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.keycloak.connections.jpa.updater.liquibase.custom;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import liquibase.datatype.DataTypeFactory;
|
||||
import liquibase.exception.CustomChangeException;
|
||||
import liquibase.statement.core.InsertStatement;
|
||||
import liquibase.statement.core.UpdateStatement;
|
||||
import liquibase.structure.core.Table;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JpaUpdate1_4_0_Final extends CustomKeycloakTask {
|
||||
|
||||
@Override
|
||||
protected void generateStatementsImpl() throws CustomChangeException {
|
||||
String userAttributeTableName = database.correctObjectName("USER_ATTRIBUTE", Table.class);
|
||||
|
||||
try {
|
||||
PreparedStatement statement = jdbcConnection.prepareStatement("select NAME, USER_ID from USER_ATTRIBUTE");
|
||||
|
||||
try {
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
while (resultSet.next()) {
|
||||
String name = resultSet.getString(1);
|
||||
String userId = resultSet.getString(2);
|
||||
|
||||
UpdateStatement updateStatement = new UpdateStatement(null, null, userAttributeTableName)
|
||||
.addNewColumnValue("ID", KeycloakModelUtils.generateId())
|
||||
.setWhereClause("NAME='" + name + "' AND USER_ID='" + userId + "'");
|
||||
statements.add(updateStatement);
|
||||
}
|
||||
} finally {
|
||||
resultSet.close();
|
||||
}
|
||||
} finally {
|
||||
statement.close();
|
||||
}
|
||||
|
||||
confirmationMessage.append("Updated " + statements.size() + " attributes in USER_ATTRIBUTE table");
|
||||
} catch (Exception e) {
|
||||
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTaskId() {
|
||||
return "Update 1.4.0.Final";
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="keycloak" id="1.2.0.Final">
|
||||
<!-- KEYCLOAK-1277 -->
|
||||
<update tableName="CLIENT">
|
||||
<column name="DIRECT_GRANTS_ONLY" valueBoolean="false"/>
|
||||
<where>DIRECT_GRANTS_ONLY is null</where>
|
||||
</update>
|
||||
<update tableName="CLIENT">
|
||||
<column name="BEARER_ONLY" valueBoolean="false"/>
|
||||
<where>BEARER_ONLY is null</where>
|
||||
</update>
|
||||
<update tableName="CLIENT">
|
||||
<column name="SURROGATE_AUTH_REQUIRED" valueBoolean="false"/>
|
||||
<where>SURROGATE_AUTH_REQUIRED is null</where>
|
||||
</update>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="bburke@redhat.com" id="1.3.0">
|
||||
<delete tableName="CLIENT_SESSION_ROLE"/>
|
||||
<delete tableName="CLIENT_SESSION_PROT_MAPPER"/>
|
||||
<delete tableName="CLIENT_SESSION_NOTE"/>
|
||||
<delete tableName="CLIENT_SESSION"/>
|
||||
<delete tableName="USER_SESSION_NOTE"/>
|
||||
<delete tableName="USER_SESSION"/>
|
||||
<createTable tableName="ADMIN_EVENT_ENTITY">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ADMIN_EVENT_TIME" type="BIGINT"/>
|
||||
<column name="REALM_ID" type="VARCHAR(255)"/>
|
||||
<column name="OPERATION_TYPE" type="VARCHAR(255)"/>
|
||||
<column name="AUTH_REALM_ID" type="VARCHAR(255)"/>
|
||||
<column name="AUTH_CLIENT_ID" type="VARCHAR(255)"/>
|
||||
<column name="AUTH_USER_ID" type="VARCHAR(255)"/>
|
||||
<column name="IP_ADDRESS" type="VARCHAR(255)"/>
|
||||
<column name="RESOURCE_PATH" type="VARCHAR(2550)"/>
|
||||
<column name="REPRESENTATION" type="TEXT(25500)"/>
|
||||
<column name="ERROR" type="VARCHAR(255)"/>
|
||||
</createTable>
|
||||
<createTable tableName="AUTHENTICATOR">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ALIAS" type="VARCHAR(255)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
||||
</createTable>
|
||||
<createTable tableName="AUTHENTICATION_FLOW">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ALIAS" type="VARCHAR(255)"/>
|
||||
<column name="DESCRIPTION" type="VARCHAR(255)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
</createTable>
|
||||
<createTable tableName="AUTHENTICATION_EXECUTION">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ALIAS" type="VARCHAR(255)"/>
|
||||
<column name="AUTHENTICATOR" type="VARCHAR(36)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
<column name="FLOW_ID" type="VARCHAR(36)"/>
|
||||
<column name="REQUIREMENT" type="INT"/>
|
||||
<column name="PRIORITY" type="INT"/>
|
||||
<column name="USER_SETUP_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="AUTHENTICATOR_FLOW" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="AUTHENTICATOR_CONFIG">
|
||||
<column name="AUTHENTICATOR_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="CLOB"/>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="USER_FEDERATION_MAPPER">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="FEDERATION_PROVIDER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="FEDERATION_MAPPER_TYPE" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="REALM_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="USER_FEDERATION_MAPPER_CONFIG">
|
||||
<column name="USER_FEDERATION_MAPPER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="VARCHAR(255)"/>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<addColumn tableName="REALM">
|
||||
<column name="ADMIN_EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ADMIN_EVENTS_DETAILS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="EDIT_USERNAME_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<createTable tableName="CLIENT_SESSION_AUTH_STATUS">
|
||||
<column name="AUTHENTICATOR" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="STATUS" type="INT"/>
|
||||
<column name="CLIENT_SESSION" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addColumn tableName="CLIENT_SESSION">
|
||||
<column name="AUTH_USER_ID" type="VARCHAR(36)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="IDENTITY_PROVIDER">
|
||||
<column name="TRUST_EMAIL" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="UPDATE_PROFILE_FIRST_LGN_MD" type="VARCHAR(255)" defaultValue="on">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<!-- migrate value from UPDATE_PROFILE_FIRST_LOGIN to UPDATE_PROFILE_FIRST_LGN_MD then drop it -->
|
||||
<update tableName="IDENTITY_PROVIDER">
|
||||
<column name="UPDATE_PROFILE_FIRST_LGN_MD" value="off"/>
|
||||
<where>UPDATE_PROFILE_FIRST_LOGIN = :value</where>
|
||||
<whereParams>
|
||||
<param valueBoolean="false" />
|
||||
</whereParams>
|
||||
</update>
|
||||
<dropDefaultValue tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LOGIN" />
|
||||
<dropColumn tableName="IDENTITY_PROVIDER" columnName="UPDATE_PROFILE_FIRST_LOGIN"/>
|
||||
|
||||
<addColumn tableName="USER_REQUIRED_ACTION">
|
||||
<column name="REQUIRED_ACTION" type="VARCHAR(255)" defaultValue=" ">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<!-- VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD -->
|
||||
<update tableName="USER_REQUIRED_ACTION">
|
||||
<column name="REQUIRED_ACTION" value="VERIFY_EMAIL"/>
|
||||
<where>ACTION = 0</where>
|
||||
</update>
|
||||
<update tableName="USER_REQUIRED_ACTION">
|
||||
<column name="REQUIRED_ACTION" value="UPDATE_PROFILE"/>
|
||||
<where>ACTION = 1</where>
|
||||
</update>
|
||||
<update tableName="USER_REQUIRED_ACTION">
|
||||
<column name="REQUIRED_ACTION" value="CONFIGURE_TOTP"/>
|
||||
<where>ACTION = 2</where>
|
||||
</update>
|
||||
<update tableName="USER_REQUIRED_ACTION">
|
||||
<column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
|
||||
<where>ACTION = 3</where>
|
||||
</update>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_PK" tableName="AUTHENTICATOR"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_FLOW_PK" tableName="AUTHENTICATION_FLOW"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTH_EXEC_PK" tableName="AUTHENTICATION_EXECUTION"/>
|
||||
<addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTH_CFG_PK" tableName="AUTHENTICATOR_CONFIG"/>
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
|
||||
<dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/>
|
||||
<addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION, AUTHENTICATOR" constraintName="CONSTRAINT_AUTH_STATUS_PK" tableName="CLIENT_SESSION_AUTH_STATUS"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/>
|
||||
<addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTH_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTH_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTH_EXEC_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="FLOW_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTH_EXEC_FLOW" referencedColumnNames="ID" referencedTableName="AUTHENTICATION_FLOW"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_FEDPRV" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_FEDERATION_MAPPER_ID" baseTableName="USER_FEDERATION_MAPPER_CONFIG" constraintName="FK_FEDMAPPER_CFG" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_MAPPER"/>
|
||||
|
||||
<dropDefaultValue tableName="REALM" columnName="PASSWORD_CRED_GRANT_ALLOWED" />
|
||||
<dropColumn tableName="REALM" columnName="PASSWORD_CRED_GRANT_ALLOWED"/>
|
||||
|
||||
<!-- KEYCLOAK-1298 Change constraint names to be upper-cased -->
|
||||
<dropForeignKeyConstraint baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" />
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_PMConfig" tableName="PROTOCOL_MAPPER_CONFIG"/>
|
||||
<addPrimaryKey columnNames="PROTOCOL_MAPPER_ID, NAME" constraintName="CONSTRAINT_PMCONFIG" tableName="PROTOCOL_MAPPER_CONFIG"/>
|
||||
<addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMCONFIG" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" />
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
|
||||
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMCONFIG" tableName="IDP_MAPPER_CONFIG"/>
|
||||
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMCONFIG" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
|
||||
<!-- Sybase specific hacks -->
|
||||
<modifySql dbms="sybase">
|
||||
<replace replace="[USER_REQUIRED_ACTION] DROP PRIMARY KEY" with="[USER_REQUIRED_ACTION] DROP CONSTRAINT CONSTRAINT_2" />
|
||||
<replace replace="[PROTOCOL_MAPPER_CONFIG] DROP PRIMARY KEY" with="[PROTOCOL_MAPPER_CONFIG] DROP CONSTRAINT CONSTRAINT_PMConfig" />
|
||||
<replace replace="[IDP_MAPPER_CONFIG] DROP PRIMARY KEY" with="[IDP_MAPPER_CONFIG] DROP CONSTRAINT CONSTRAINT_IDPMConfig" />
|
||||
<regExpReplace replace=".*(SET DEFAULT NULL)" with="SELECT 1" />
|
||||
</modifySql>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="bburke@redhat.com" id="1.4.0">
|
||||
<delete tableName="CLIENT_SESSION_AUTH_STATUS"/>
|
||||
<delete tableName="CLIENT_SESSION_ROLE"/>
|
||||
<delete tableName="CLIENT_SESSION_PROT_MAPPER"/>
|
||||
<delete tableName="CLIENT_SESSION_NOTE"/>
|
||||
<delete tableName="CLIENT_SESSION"/>
|
||||
<delete tableName="USER_SESSION_NOTE"/>
|
||||
<delete tableName="USER_SESSION"/>
|
||||
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="SERVICE_ACCOUNTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="AUTHENTICATION_FLOW">
|
||||
<column name="PROVIDER_ID" type="VARCHAR(36)" defaultValue="basic-flow">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="TOP_LEVEL" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="BUILT_IN" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="AUTHENTICATION_EXECUTION">
|
||||
<column name="AUTH_FLOW_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="AUTH_CONFIG" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="USER_ATTRIBUTE">
|
||||
<column name="ID" type="VARCHAR(36)" defaultValue="sybase-needs-something-here">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
|
||||
<dropColumn tableName="AUTHENTICATOR" columnName="PROVIDER_ID"/>
|
||||
<renameTable oldTableName="AUTHENTICATOR_CONFIG" newTableName="AUTHENTICATOR_CONFIG_ENTRY"/>
|
||||
<renameTable oldTableName="AUTHENTICATOR" newTableName="AUTHENTICATOR_CONFIG"/>
|
||||
<!-- OAUTH_GRANT,
|
||||
CODE_TO_TOKEN,
|
||||
VERIFY_EMAIL,
|
||||
UPDATE_PROFILE,
|
||||
CONFIGURE_TOTP,
|
||||
UPDATE_PASSWORD,
|
||||
RECOVER_PASSWORD,
|
||||
AUTHENTICATE,
|
||||
SOCIAL_CALLBACK,
|
||||
LOGGED_OUT -->
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="OAUTH_GRANT"/>
|
||||
<where>ACTION = 0</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="CODE_TO_TOKEN"/>
|
||||
<where>ACTION = 1</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="VERIFY_EMAIL"/>
|
||||
<where>ACTION = 2</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="UPDATE_PROFILE"/>
|
||||
<where>ACTION = 3</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="CONFIGURE_TOTP"/>
|
||||
<where>ACTION = 4</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="UPDATE_PASSWORD"/>
|
||||
<where>ACTION = 5</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="RECOVER_PASSWORD"/>
|
||||
<where>ACTION = 6</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="AUTHENTICATE"/>
|
||||
<where>ACTION = 7</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="SOCIAL_CALLBACK"/>
|
||||
<where>ACTION = 8</where>
|
||||
</update>
|
||||
<update tableName="CLIENT_SESSION">
|
||||
<column name="CURRENT_ACTION" value="LOGGED_OUT"/>
|
||||
<where>ACTION = 9</where>
|
||||
</update>
|
||||
|
||||
<createTable tableName="CLIENT_USER_SESSION_NOTE">
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="VARCHAR(2048)"/>
|
||||
<column name="CLIENT_SESSION" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="REQUIRED_ACTION_PROVIDER">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ALIAS" type="VARCHAR(255)"/>
|
||||
<column name="NAME" type="VARCHAR(255)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="DEFAULT_ACTION" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
||||
</createTable>
|
||||
<createTable tableName="REQUIRED_ACTION_CONFIG">
|
||||
<column name="REQUIRED_ACTION_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="CLOB"/>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate1_4_0_Final"/>
|
||||
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_6" tableName="USER_ATTRIBUTE"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_USER_ATTRIBUTE_PK" tableName="USER_ATTRIBUTE"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_REQ_ACT_PRV_PK" tableName="REQUIRED_ACTION_PROVIDER"/>
|
||||
<addPrimaryKey columnNames="REQUIRED_ACTION_ID, NAME" constraintName="CONSTRAINT_REQ_ACT_CFG_PK" tableName="REQUIRED_ACTION_CONFIG"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTR_CL_USR_SES_NOTE" tableName="CLIENT_USER_SESSION_NOTE"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REQUIRED_ACTION_PROVIDER" constraintName="FK_REQ_ACT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
|
||||
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
|
||||
<addColumn tableName="USER_ENTITY">
|
||||
<column name="CREATED_TIMESTAMP" type="BIGINT"/>
|
||||
<column name="SERVICE_ACCOUNT_CLIENT_LINK" type="VARCHAR(36)"/>
|
||||
</addColumn>
|
||||
|
||||
<!-- Sybase specific hacks -->
|
||||
<modifySql dbms="sybase">
|
||||
<replace replace="[USER_ATTRIBUTE] DROP PRIMARY KEY" with="[USER_ATTRIBUTE] DROP CONSTRAINT CONSTRAINT_6" />
|
||||
</modifySql>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -5,4 +5,7 @@
|
||||
<include file="META-INF/jpa-changelog-1.1.0.Final.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.Beta1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.CR1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.Final.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.3.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.4.0.xml"/>
|
||||
</databaseChangeLog>
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -24,7 +24,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
+103
-76
@@ -13,10 +13,12 @@ import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,8 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||
|
||||
private Config.Scope config;
|
||||
|
||||
private Map<String,String> operationalInfo;
|
||||
|
||||
@Override
|
||||
public JpaConnectionProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
@@ -70,106 +74,124 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||
|
||||
Connection connection = null;
|
||||
|
||||
String unitName = config.get("unitName");
|
||||
String databaseSchema = config.get("databaseSchema");
|
||||
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
|
||||
// Only load config from keycloak-server.json if unitName is not specified
|
||||
if (unitName == null) {
|
||||
unitName = "keycloak-default";
|
||||
String unitName = "keycloak-default";
|
||||
|
||||
String dataSource = config.get("dataSource");
|
||||
if (dataSource != null) {
|
||||
if (config.getBoolean("jta", false)) {
|
||||
properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
|
||||
} else {
|
||||
properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
|
||||
}
|
||||
String dataSource = config.get("dataSource");
|
||||
if (dataSource != null) {
|
||||
if (config.getBoolean("jta", false)) {
|
||||
properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
|
||||
} else {
|
||||
properties.put(AvailableSettings.JDBC_URL, config.get("url"));
|
||||
properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
|
||||
|
||||
String user = config.get("user");
|
||||
if (user != null) {
|
||||
properties.put(AvailableSettings.JDBC_USER, user);
|
||||
}
|
||||
String password = config.get("password");
|
||||
if (password != null) {
|
||||
properties.put(AvailableSettings.JDBC_PASSWORD, password);
|
||||
}
|
||||
properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
|
||||
}
|
||||
} else {
|
||||
properties.put(AvailableSettings.JDBC_URL, config.get("url"));
|
||||
properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
|
||||
|
||||
String driverDialect = config.get("driverDialect");
|
||||
if (driverDialect != null && driverDialect.length() > 0) {
|
||||
properties.put("hibernate.dialect", driverDialect);
|
||||
String user = config.get("user");
|
||||
if (user != null) {
|
||||
properties.put(AvailableSettings.JDBC_USER, user);
|
||||
}
|
||||
|
||||
if (databaseSchema != null) {
|
||||
if (databaseSchema.equals("development-update")) {
|
||||
properties.put("hibernate.hbm2ddl.auto", "update");
|
||||
databaseSchema = null;
|
||||
} else if (databaseSchema.equals("development-validate")) {
|
||||
properties.put("hibernate.hbm2ddl.auto", "validate");
|
||||
databaseSchema = null;
|
||||
}
|
||||
String password = config.get("password");
|
||||
if (password != null) {
|
||||
properties.put(AvailableSettings.JDBC_PASSWORD, password);
|
||||
}
|
||||
}
|
||||
|
||||
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
|
||||
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
|
||||
String driverDialect = config.get("driverDialect");
|
||||
if (driverDialect != null && driverDialect.length() > 0) {
|
||||
properties.put("hibernate.dialect", driverDialect);
|
||||
}
|
||||
|
||||
String schema = config.get("schema");
|
||||
if (schema != null) {
|
||||
properties.put("hibernate.default_schema", schema);
|
||||
}
|
||||
|
||||
if (databaseSchema != null) {
|
||||
logger.trace("Updating database");
|
||||
|
||||
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
|
||||
if (updater == null) {
|
||||
throw new RuntimeException("Can't update database: JPA updater provider not found");
|
||||
if (databaseSchema.equals("development-update")) {
|
||||
properties.put("hibernate.hbm2ddl.auto", "update");
|
||||
databaseSchema = null;
|
||||
} else if (databaseSchema.equals("development-validate")) {
|
||||
properties.put("hibernate.hbm2ddl.auto", "validate");
|
||||
databaseSchema = null;
|
||||
}
|
||||
|
||||
connection = getConnection();
|
||||
|
||||
if (databaseSchema.equals("update")) {
|
||||
String currentVersion = null;
|
||||
try {
|
||||
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql());
|
||||
if (resultSet.next()) {
|
||||
currentVersion = resultSet.getString(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
|
||||
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
|
||||
updater.update(session, connection);
|
||||
} else {
|
||||
logger.debug("Database is up to date");
|
||||
}
|
||||
} else if (databaseSchema.equals("validate")) {
|
||||
updater.validate(connection);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
||||
}
|
||||
|
||||
logger.trace("Database update completed");
|
||||
}
|
||||
|
||||
logger.trace("Creating EntityManagerFactory");
|
||||
emf = Persistence.createEntityManagerFactory(unitName, properties);
|
||||
logger.trace("EntityManagerFactory created");
|
||||
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
|
||||
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
|
||||
|
||||
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
logger.warn(e);
|
||||
}
|
||||
connection = getConnection();
|
||||
try{
|
||||
prepareOperationalInfo(connection);
|
||||
|
||||
if (databaseSchema != null) {
|
||||
logger.trace("Updating database");
|
||||
|
||||
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
|
||||
if (updater == null) {
|
||||
throw new RuntimeException("Can't update database: JPA updater provider not found");
|
||||
}
|
||||
|
||||
if (databaseSchema.equals("update")) {
|
||||
String currentVersion = null;
|
||||
try {
|
||||
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql(schema));
|
||||
if (resultSet.next()) {
|
||||
currentVersion = resultSet.getString(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
|
||||
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
|
||||
updater.update(session, connection, schema);
|
||||
} else {
|
||||
logger.debug("Database is up to date");
|
||||
}
|
||||
} else if (databaseSchema.equals("validate")) {
|
||||
updater.validate(connection, schema);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
|
||||
}
|
||||
|
||||
logger.trace("Database update completed");
|
||||
}
|
||||
|
||||
logger.trace("Creating EntityManagerFactory");
|
||||
emf = Persistence.createEntityManagerFactory(unitName, properties);
|
||||
logger.trace("EntityManagerFactory created");
|
||||
|
||||
} finally {
|
||||
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
logger.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepareOperationalInfo(Connection connection) {
|
||||
try {
|
||||
operationalInfo = new LinkedHashMap<>();
|
||||
DatabaseMetaData md = connection.getMetaData();
|
||||
operationalInfo.put("databaseUrl",md.getURL());
|
||||
operationalInfo.put("databaseUser", md.getUserName());
|
||||
operationalInfo.put("databaseProduct", md.getDatabaseProductName() + " " + md.getDatabaseProductVersion());
|
||||
operationalInfo.put("databaseDriver", md.getDriverName() + " " + md.getDriverVersion());
|
||||
} catch (SQLException e) {
|
||||
logger.warn("Unable to prepare operational info due database exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Connection getConnection() {
|
||||
try {
|
||||
String dataSourceLookup = config.get("dataSource");
|
||||
@@ -185,4 +207,9 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,String> getOperationalInfo() {
|
||||
return operationalInfo;
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
|
||||
public interface JpaConnectionProviderFactory extends ServerInfoAwareProviderFactory<JpaConnectionProvider> {
|
||||
|
||||
}
|
||||
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class JpaConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsJpa";
|
||||
|
||||
+4
-4
@@ -12,12 +12,12 @@ public interface JpaUpdaterProvider extends Provider {
|
||||
|
||||
public String FIRST_VERSION = "1.0.0.Final";
|
||||
|
||||
public String LAST_VERSION = "1.2.0.RC1";
|
||||
public String LAST_VERSION = "1.4.0";
|
||||
|
||||
public String getCurrentVersionSql();
|
||||
public String getCurrentVersionSql(String defaultSchema);
|
||||
|
||||
public void update(KeycloakSession session, Connection connection);
|
||||
public void update(KeycloakSession session, Connection connection, String defaultSchema);
|
||||
|
||||
public void validate(Connection connection);
|
||||
public void validate(Connection connection, String defaultSchema);
|
||||
|
||||
}
|
||||
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class JpaUpdaterSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsJpaUpdater";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserFederationMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
|
||||
@@ -24,18 +25,25 @@
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.AuthenticatorConfigEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredActionProviderEntity</class>
|
||||
|
||||
<!-- JpaUserSessionProvider -->
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionAuthStatusEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
|
||||
|
||||
<!-- JpaAuditProvider -->
|
||||
<!-- JpaAuditProviders -->
|
||||
<class>org.keycloak.events.jpa.EventEntity</class>
|
||||
<class>org.keycloak.events.jpa.AdminEventEntity</class>
|
||||
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+4
-6
@@ -6,11 +6,7 @@ import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_CR1;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.*;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -30,7 +26,9 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
|
||||
Update1_0_0_Final.class,
|
||||
Update1_1_0_Beta1.class,
|
||||
Update1_2_0_Beta1.class,
|
||||
Update1_2_0_CR1.class
|
||||
Update1_2_0_CR1.class,
|
||||
Update1_3_0.class,
|
||||
Update1_4_0.class
|
||||
};
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -53,6 +53,10 @@ public abstract class Update {
|
||||
log.debugv("Deleted entries from {0}", collection);
|
||||
}
|
||||
|
||||
protected void removeField(String collection, String field) {
|
||||
db.getCollection(collection).update(new BasicDBObject(), new BasicDBObject("$unset" , new BasicDBObject(field, 1)), false, true);
|
||||
}
|
||||
|
||||
protected void renameCollection(String collection, String newName) {
|
||||
db.getCollection(collection).rename(newName);
|
||||
}
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ public class Update1_2_0_Beta1 extends Update {
|
||||
dbMapper.put("protocolMapper", protocolMapper.getProtocolMapper());
|
||||
|
||||
Map<String, String> config = protocolMapper.getConfig();
|
||||
BasicDBObject dbConfig = MapMapper.convertMap(config);
|
||||
BasicDBObject dbConfig = MapMapper.convertMap(config, null);
|
||||
dbMapper.put("config", dbConfig);
|
||||
|
||||
dbProtocolMappers.add(dbMapper);
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Update1_3_0 extends Update {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1.3.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(KeycloakSession session) {
|
||||
deleteEntries("clientSessions");
|
||||
deleteEntries("sessions");
|
||||
|
||||
removeField("realms", "passwordCredentialGrantAllowed");
|
||||
|
||||
updateIdentityProviders();
|
||||
}
|
||||
|
||||
private void updateIdentityProviders() {
|
||||
DBCollection realms = db.getCollection("realms");
|
||||
DBCursor realmsCursor = realms.find();
|
||||
|
||||
try {
|
||||
while (realmsCursor.hasNext()) {
|
||||
BasicDBObject realm = (BasicDBObject) realmsCursor.next();
|
||||
|
||||
BasicDBList identityProviders = (BasicDBList) realm.get("identityProviders");
|
||||
if (identityProviders != null) {
|
||||
for (Object ipObj : identityProviders) {
|
||||
BasicDBObject identityProvider = (BasicDBObject) ipObj;
|
||||
|
||||
boolean updateProfileFirstLogin = identityProvider.getBoolean("updateProfileFirstLogin");
|
||||
String upflMode = updateProfileFirstLogin ? IdentityProviderRepresentation.UPFLM_ON : IdentityProviderRepresentation.UPFLM_OFF;
|
||||
identityProvider.put("updateProfileFirstLoginMode", upflMode);
|
||||
identityProvider.removeField("updateProfileFirstLogin");
|
||||
|
||||
identityProvider.put("trustEmail", false);
|
||||
}
|
||||
}
|
||||
|
||||
realms.save(realm);
|
||||
}
|
||||
} finally {
|
||||
realmsCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Update1_4_0 extends Update {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1.4.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(KeycloakSession session) throws ClassNotFoundException {
|
||||
deleteEntries("clientSessions");
|
||||
deleteEntries("sessions");
|
||||
|
||||
// Remove warning
|
||||
removeField("realms", "authenticators");
|
||||
|
||||
updateUserAttributes();
|
||||
}
|
||||
|
||||
private void updateUserAttributes() {
|
||||
DBCollection users = db.getCollection("users");
|
||||
DBCursor usersCursor = users.find();
|
||||
|
||||
try {
|
||||
while (usersCursor.hasNext()) {
|
||||
BasicDBObject user = (BasicDBObject) usersCursor.next();
|
||||
|
||||
BasicDBObject attributes = (BasicDBObject) user.get("attributes");
|
||||
if (attributes != null) {
|
||||
for (Map.Entry<String, Object> attr : new HashSet<>(attributes.entrySet())) {
|
||||
String attrName = attr.getKey();
|
||||
Object attrValue = attr.getValue();
|
||||
if (attrValue != null && attrValue instanceof String) {
|
||||
BasicDBList asList = new BasicDBList();
|
||||
asList.add(attrValue);
|
||||
attributes.put(attrName, asList);
|
||||
}
|
||||
}
|
||||
|
||||
user.put("attributes", attributes);
|
||||
|
||||
users.save(user);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
usersCursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+58
-18
@@ -5,6 +5,7 @@ import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientOptions;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
@@ -16,7 +17,10 @@ import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
@@ -38,10 +42,15 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
|
||||
"org.keycloak.models.entities.UserFederationProviderEntity",
|
||||
"org.keycloak.models.entities.UserFederationMapperEntity",
|
||||
"org.keycloak.models.entities.ProtocolMapperEntity",
|
||||
"org.keycloak.models.entities.IdentityProviderMapperEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity"
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
|
||||
"org.keycloak.models.entities.AuthenticationExecutionEntity",
|
||||
"org.keycloak.models.entities.AuthenticationFlowEntity",
|
||||
"org.keycloak.models.entities.AuthenticatorConfigEntity",
|
||||
"org.keycloak.models.entities.RequiredActionProviderEntity",
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
|
||||
@@ -50,7 +59,9 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
|
||||
private MongoStore mongoStore;
|
||||
private DB db;
|
||||
private Config.Scope config;
|
||||
protected Config.Scope config;
|
||||
|
||||
private Map<String,String> operationalInfo;
|
||||
|
||||
@Override
|
||||
public MongoConnectionProvider create(KeycloakSession session) {
|
||||
@@ -77,21 +88,9 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
synchronized (this) {
|
||||
if (client == null) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
this.client = createMongoClient();
|
||||
|
||||
String dbName = config.get("db", "keycloak");
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
}
|
||||
|
||||
this.db = client.getDB(dbName);
|
||||
|
||||
String databaseSchema = config.get("databaseSchema");
|
||||
@@ -110,8 +109,6 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
}
|
||||
|
||||
this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -140,6 +137,44 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
return "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you want more possibility to configure Mongo client. It can be also used to inject mongo client
|
||||
* from different source.
|
||||
*
|
||||
* This method can assume that "config" is already set and can use it.
|
||||
*
|
||||
* @return mongoClient instance, which will be shared for whole Keycloak
|
||||
*
|
||||
* @throws UnknownHostException
|
||||
*/
|
||||
protected MongoClient createMongoClient() throws UnknownHostException {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak");
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
|
||||
MongoClient client;
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
}
|
||||
|
||||
operationalInfo = new LinkedHashMap<>();
|
||||
operationalInfo.put("mongoServerAddress", client.getAddress().toString());
|
||||
operationalInfo.put("mongoDatabaseName", dbName);
|
||||
operationalInfo.put("mongoUser", user);
|
||||
operationalInfo.put("mongoDriverVersion", client.getVersion());
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
return client;
|
||||
}
|
||||
|
||||
protected MongoClientOptions getClientOptions() {
|
||||
MongoClientOptions.Builder builder = MongoClientOptions.builder();
|
||||
checkIntOption("connectionsPerHost", builder);
|
||||
@@ -183,4 +218,9 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,String> getOperationalInfo() {
|
||||
return operationalInfo;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.ServerInfoAwareProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
|
||||
public interface MongoConnectionProviderFactory extends ServerInfoAwareProviderFactory<MongoConnectionProvider> {
|
||||
}
|
||||
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class MongoConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsMongo";
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -14,9 +15,9 @@ public class MapperContext<T, S> {
|
||||
private final Class<? extends S> expectedReturnType;
|
||||
|
||||
// in case that expected return type is generic type (like "List<String>"), then genericTypes could contain list of expected generic arguments
|
||||
private final List<Class<?>> genericTypes;
|
||||
private final List<Type> genericTypes;
|
||||
|
||||
public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Class<?>> genericTypes) {
|
||||
public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Type> genericTypes) {
|
||||
this.objectToConvert = objectToConvert;
|
||||
this.expectedReturnType = expectedReturnType;
|
||||
this.genericTypes = genericTypes;
|
||||
@@ -30,7 +31,7 @@ public class MapperContext<T, S> {
|
||||
return expectedReturnType;
|
||||
}
|
||||
|
||||
public List<Class<?>> getGenericTypes() {
|
||||
public List<Type> getGenericTypes() {
|
||||
return genericTypes;
|
||||
}
|
||||
}
|
||||
+8
@@ -47,6 +47,10 @@ public class MapperRegistry {
|
||||
|
||||
|
||||
public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) {
|
||||
if (context.getObjectToConvert() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object dbObject = context.getObjectToConvert();
|
||||
Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
|
||||
|
||||
@@ -74,6 +78,10 @@ public class MapperRegistry {
|
||||
|
||||
|
||||
public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
|
||||
if (applicationObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> appObjectType = applicationObject.getClass();
|
||||
Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers);
|
||||
if (mapper == null) {
|
||||
|
||||
+3
-3
@@ -79,9 +79,9 @@ public class MongoStoreImpl implements MongoStore {
|
||||
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, Set.class));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBListToSetMapper(mapperRegistry));
|
||||
|
||||
mapperRegistry.addAppObjectMapper(new MapMapper(HashMap.class));
|
||||
mapperRegistry.addAppObjectMapper(new MapMapper(Map.class));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper());
|
||||
mapperRegistry.addAppObjectMapper(new MapMapper(mapperRegistry, HashMap.class));
|
||||
mapperRegistry.addAppObjectMapper(new MapMapper(mapperRegistry, Map.class));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper(mapperRegistry));
|
||||
|
||||
// Enum converters
|
||||
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class BasicDBListMapper implements Mapper<BasicDBList, List> {
|
||||
public List convertObject(MapperContext<BasicDBList, List> context) {
|
||||
BasicDBList dbList = context.getObjectToConvert();
|
||||
ArrayList<Object> appObjects = new ArrayList<Object>();
|
||||
Class<?> expectedListElementType = context.getGenericTypes().get(0);
|
||||
Class<?> expectedListElementType = (Class<?>) context.getGenericTypes().get(0);
|
||||
|
||||
for (Object dbObject : dbList) {
|
||||
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ public class BasicDBListToSetMapper implements Mapper<BasicDBList, Set> {
|
||||
public Set convertObject(MapperContext<BasicDBList, Set> context) {
|
||||
BasicDBList dbList = context.getObjectToConvert();
|
||||
Set<Object> appObjects = new HashSet<Object>();
|
||||
Class<?> expectedListElementType = context.getGenericTypes().get(0);
|
||||
Class<?> expectedListElementType = (Class<?>) context.getGenericTypes().get(0);
|
||||
|
||||
for (Object dbObject : dbList) {
|
||||
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
|
||||
|
||||
+2
-4
@@ -14,6 +14,7 @@ import org.keycloak.util.reflections.Types;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -87,10 +88,7 @@ public class BasicDBObjectMapper<S> implements Mapper<BasicDBObject, S> {
|
||||
ParameterizedType parameterized = (ParameterizedType) type;
|
||||
Type[] genericTypeArguments = parameterized.getActualTypeArguments();
|
||||
|
||||
List<Class<?>> genericTypes = new ArrayList<Class<?>>();
|
||||
for (Type genericType : genericTypeArguments) {
|
||||
genericTypes.add((Class<?>)genericType);
|
||||
}
|
||||
List<Type> genericTypes = Arrays.asList(genericTypeArguments);
|
||||
|
||||
Class<?> expectedReturnType = (Class<?>)parameterized.getRawType();
|
||||
context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, genericTypes);
|
||||
|
||||
+32
-4
@@ -3,31 +3,44 @@ package org.keycloak.connections.mongo.impl.types;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* For now, there is support just for convert to Map<String, simpleType>
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
|
||||
|
||||
private final MapperRegistry mapperRegistry;
|
||||
|
||||
public BasicDBObjectToMapMapper(MapperRegistry mapperRegistry) {
|
||||
this.mapperRegistry = mapperRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map convertObject(MapperContext<BasicDBObject, Map> context) {
|
||||
BasicDBObject dbObjectToConvert = context.getObjectToConvert();
|
||||
Type expectedElementValueType = context.getGenericTypes().get(1);
|
||||
|
||||
HashMap<String, Object> result = new HashMap<String, Object>();
|
||||
for (Map.Entry<String, Object> entry : dbObjectToConvert.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
Object dbValue = entry.getValue();
|
||||
|
||||
// Workaround as manually inserted numbers into mongo may be treated as "Double"
|
||||
if (value instanceof Double && context.getGenericTypes().get(1) == Integer.class) {
|
||||
value = ((Double)value).intValue();
|
||||
if (dbValue instanceof Double && expectedElementValueType == Integer.class) {
|
||||
dbValue = ((Double)dbValue).intValue();
|
||||
}
|
||||
|
||||
MapperContext<Object, Object> newContext = getMapperContext(dbValue, expectedElementValueType);
|
||||
Object value = mapperRegistry.convertDBObjectToApplicationObject(newContext);
|
||||
|
||||
if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
|
||||
key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
|
||||
}
|
||||
@@ -46,4 +59,19 @@ public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
|
||||
public Class<Map> getExpectedReturnType() {
|
||||
return Map.class;
|
||||
}
|
||||
|
||||
private MapperContext<Object, Object> getMapperContext(Object dbValue, Type expectedElementValueType) {
|
||||
if (expectedElementValueType instanceof Class) {
|
||||
Class<?> clazz = (Class<?>) expectedElementValueType;
|
||||
return new MapperContext<>(dbValue, clazz, null);
|
||||
} else if (expectedElementValueType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterized = (ParameterizedType) expectedElementValueType;
|
||||
Class<?> expectedClazz = (Class<?>) parameterized.getRawType();
|
||||
Type[] generics = parameterized.getActualTypeArguments();
|
||||
|
||||
return new MapperContext<>(dbValue, expectedClazz, Arrays.asList(generics));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected type: '" + expectedElementValueType + "' for converting " + dbValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -3,6 +3,7 @@ package org.keycloak.connections.mongo.impl.types;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -17,30 +18,34 @@ public class MapMapper<T extends Map> implements Mapper<T, BasicDBObject> {
|
||||
// Just some dummy way of encoding . character as it's not allowed by mongo in key fields
|
||||
static final String DOT_PLACEHOLDER = "###";
|
||||
|
||||
private final MapperRegistry mapperRegistry;
|
||||
private final Class<T> mapType;
|
||||
|
||||
public MapMapper(Class<T> mapType) {
|
||||
public MapMapper(MapperRegistry mapperRegistry, Class<T> mapType) {
|
||||
this.mapperRegistry = mapperRegistry;
|
||||
this.mapType = mapType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
|
||||
T mapToConvert = context.getObjectToConvert();
|
||||
return convertMap(mapToConvert);
|
||||
return convertMap(mapToConvert, mapperRegistry);
|
||||
}
|
||||
|
||||
public static BasicDBObject convertMap(Map mapToConvert) {
|
||||
public static BasicDBObject convertMap(Map mapToConvert, MapperRegistry mapperRegistry) {
|
||||
BasicDBObject dbObject = new BasicDBObject();
|
||||
Set<Map.Entry> entries = mapToConvert.entrySet();
|
||||
for (Map.Entry entry : entries) {
|
||||
String key = (String)entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
Object dbValue = mapperRegistry==null ? entry.getValue() : mapperRegistry.convertApplicationObjectToDBObject(value, Object.class);
|
||||
|
||||
if (key.contains(".")) {
|
||||
key = key.replaceAll("\\.", DOT_PLACEHOLDER);
|
||||
}
|
||||
|
||||
dbObject.put(key, value);
|
||||
dbObject.put(key, dbValue);
|
||||
}
|
||||
return dbObject;
|
||||
}
|
||||
|
||||
+5
@@ -9,6 +9,11 @@ import org.keycloak.provider.Spi;
|
||||
*/
|
||||
public class MongoUpdaterSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsMongoUpdater";
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
</parent>
|
||||
<name>Connections Parent</name>
|
||||
<description/>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.RC1-SNAPSHOT</version>
|
||||
<version>1.4.0.Final</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -29,6 +29,8 @@ public interface OAuth2Constants {
|
||||
|
||||
String PASSWORD = "password";
|
||||
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.keycloak.constants;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ServiceAccountConstants {
|
||||
|
||||
String CLIENT_AUTH = "client_auth";
|
||||
|
||||
String SERVICE_ACCOUNT_USER_PREFIX = "service-account-";
|
||||
|
||||
String CLIENT_ID_PROTOCOL_MAPPER = "Client ID";
|
||||
String CLIENT_HOST_PROTOCOL_MAPPER = "Client Host";
|
||||
String CLIENT_ADDRESS_PROTOCOL_MAPPER = "Client IP Address";
|
||||
String CLIENT_ID = "clientId";
|
||||
String CLIENT_HOST = "clientHost";
|
||||
String CLIENT_ADDRESS = "clientAddress";
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.keycloak.jose.jws;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
@@ -11,6 +12,7 @@ import java.io.Serializable;
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class JWSHeader implements Serializable {
|
||||
@JsonProperty("alg")
|
||||
private Algorithm algorithm;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class HMACProvider implements SignatureProvider {
|
||||
|
||||
public static boolean verify(JWSInput input, SecretKey key) {
|
||||
try {
|
||||
byte[] signature = sign(input.getContent(), input.getHeader().getAlgorithm(), key);
|
||||
byte[] signature = sign(input.getEncodedSignatureInput().getBytes("UTF-8"), input.getHeader().getAlgorithm(), key);
|
||||
String x = Base64Url.encode(signature);
|
||||
return x.equals(input.getEncodedSignature());
|
||||
} catch (Exception e) {
|
||||
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AuthenticationExecutionRepresentation implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String authenticatorConfig;
|
||||
private String authenticator;
|
||||
private String flowAlias;
|
||||
private boolean autheticatorFlow;
|
||||
private String requirement;
|
||||
private boolean userSetupAllowed;
|
||||
private int priority;
|
||||
|
||||
public String getAuthenticatorConfig() {
|
||||
return authenticatorConfig;
|
||||
}
|
||||
|
||||
public void setAuthenticatorConfig(String authenticatorConfig) {
|
||||
this.authenticatorConfig = authenticatorConfig;
|
||||
}
|
||||
|
||||
public String getAuthenticator() {
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
public void setAuthenticator(String authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
public String getRequirement() {
|
||||
return requirement;
|
||||
}
|
||||
|
||||
public void setRequirement(String requirement) {
|
||||
this.requirement = requirement;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public boolean isUserSetupAllowed() {
|
||||
return userSetupAllowed;
|
||||
}
|
||||
|
||||
public void setUserSetupAllowed(boolean userSetupAllowed) {
|
||||
this.userSetupAllowed = userSetupAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this execution is a flow, this is the flowId pointing to an AuthenticationFlowModel
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getFlowAlias() {
|
||||
return flowAlias;
|
||||
}
|
||||
|
||||
public void setFlowAlias(String flowId) {
|
||||
this.flowAlias = flowId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the referenced authenticator a flow?
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAutheticatorFlow() {
|
||||
return autheticatorFlow;
|
||||
}
|
||||
|
||||
public void setAutheticatorFlow(boolean autheticatorFlow) {
|
||||
this.autheticatorFlow = autheticatorFlow;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AuthenticationFlowRepresentation implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String alias;
|
||||
private String description;
|
||||
private String providerId;
|
||||
private boolean topLevel;
|
||||
private boolean builtIn;
|
||||
protected List<AuthenticationExecutionRepresentation> authenticationExecutions;
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
public boolean isTopLevel() {
|
||||
return topLevel;
|
||||
}
|
||||
|
||||
public void setTopLevel(boolean topLevel) {
|
||||
this.topLevel = topLevel;
|
||||
}
|
||||
|
||||
public boolean isBuiltIn() {
|
||||
return builtIn;
|
||||
}
|
||||
|
||||
public void setBuiltIn(boolean builtIn) {
|
||||
this.builtIn = builtIn;
|
||||
}
|
||||
|
||||
public List<AuthenticationExecutionRepresentation> getAuthenticationExecutions() {
|
||||
return authenticationExecutions;
|
||||
}
|
||||
|
||||
public void setAuthenticationExecutions(List<AuthenticationExecutionRepresentation> authenticationExecutions) {
|
||||
this.authenticationExecutions = authenticationExecutions;
|
||||
}
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AuthenticatorConfigRepresentation implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String alias;
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class ClientRepresentation {
|
||||
protected Integer notBefore;
|
||||
protected Boolean bearerOnly;
|
||||
protected Boolean consentRequired;
|
||||
protected Boolean serviceAccountsEnabled;
|
||||
protected Boolean directGrantsOnly;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean frontchannelLogout;
|
||||
@@ -144,6 +145,14 @@ public class ClientRepresentation {
|
||||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public Boolean isServiceAccountsEnabled() {
|
||||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
|
||||
this.serviceAccountsEnabled = serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public Boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
||||
+50
-5
@@ -29,7 +29,26 @@ public class IdentityProviderRepresentation {
|
||||
protected String internalId;
|
||||
protected String providerId;
|
||||
protected boolean enabled = true;
|
||||
protected boolean updateProfileFirstLogin = true;
|
||||
|
||||
public static final String UPFLM_ON = "on";
|
||||
public static final String UPFLM_MISSING = "missing";
|
||||
public static final String UPFLM_OFF = "off";
|
||||
|
||||
/**
|
||||
* Mode of profile update after first login when user is created over this identity provider. Possible values:
|
||||
* <ul>
|
||||
* <li><code>on</code> - update profile page is presented for all users
|
||||
* <li><code>missing</code> - update profile page is presented for users with missing some of mandatory user profile fields
|
||||
* <li><code>off</code> - update profile page is newer shown after first login
|
||||
* </ul>
|
||||
*
|
||||
* @see #UPFLM_ON
|
||||
* @see #UPFLM_MISSING
|
||||
* @see #UPFLM_OFF
|
||||
*/
|
||||
protected String updateProfileFirstLoginMode = UPFLM_ON;
|
||||
|
||||
protected boolean trustEmail;
|
||||
protected boolean storeToken;
|
||||
protected boolean addReadTokenRoleOnCreate;
|
||||
protected boolean authenticateByDefault;
|
||||
@@ -75,12 +94,29 @@ public class IdentityProviderRepresentation {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isUpdateProfileFirstLogin() {
|
||||
return this.updateProfileFirstLogin;
|
||||
/**
|
||||
*
|
||||
* Deprecated because replaced by {@link #updateProfileFirstLoginMode}. Kept here to allow import of old realms.
|
||||
*
|
||||
* @deprecated {@link #setUpdateProfileFirstLoginMode(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLogin ? UPFLM_ON : UPFLM_OFF;
|
||||
}
|
||||
|
||||
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
|
||||
this.updateProfileFirstLogin = updateProfileFirstLogin;
|
||||
/**
|
||||
* @return see {@link #updateProfileFirstLoginMode}
|
||||
*/
|
||||
public String getUpdateProfileFirstLoginMode() {
|
||||
return updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param updateProfileFirstLoginMode see {@link #updateProfileFirstLoginMode}
|
||||
*/
|
||||
public void setUpdateProfileFirstLoginMode(String updateProfileFirstLoginMode) {
|
||||
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
|
||||
}
|
||||
|
||||
public boolean isAuthenticateByDefault() {
|
||||
@@ -106,4 +142,13 @@ public class IdentityProviderRepresentation {
|
||||
public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) {
|
||||
this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate;
|
||||
}
|
||||
|
||||
public boolean isTrustEmail() {
|
||||
return trustEmail;
|
||||
}
|
||||
|
||||
public void setTrustEmail(boolean trustEmail) {
|
||||
this.trustEmail = trustEmail;
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -12,6 +12,9 @@ public class RealmEventsConfigRepresentation {
|
||||
protected List<String> eventsListeners;
|
||||
protected List<String> enabledEventTypes;
|
||||
|
||||
protected Boolean adminEventsEnabled;
|
||||
protected Boolean adminEventsDetailsEnabled;
|
||||
|
||||
public boolean isEventsEnabled() {
|
||||
return eventsEnabled;
|
||||
}
|
||||
@@ -43,4 +46,21 @@ public class RealmEventsConfigRepresentation {
|
||||
public void setEnabledEventTypes(List<String> enabledEventTypes) {
|
||||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,12 +18,14 @@ public class RealmRepresentation {
|
||||
protected Integer accessCodeLifespanLogin;
|
||||
protected Boolean enabled;
|
||||
protected String sslRequired;
|
||||
@Deprecated
|
||||
protected Boolean passwordCredentialGrantAllowed;
|
||||
protected Boolean registrationAllowed;
|
||||
protected Boolean registrationEmailAsUsername;
|
||||
protected Boolean rememberMe;
|
||||
protected Boolean verifyEmail;
|
||||
protected Boolean resetPasswordAllowed;
|
||||
protected Boolean editUsernameAllowed;
|
||||
|
||||
protected Boolean userCacheEnabled;
|
||||
protected Boolean realmCacheEnabled;
|
||||
@@ -44,6 +46,7 @@ public class RealmRepresentation {
|
||||
protected String codeSecret;
|
||||
protected RolesRepresentation roles;
|
||||
protected List<String> defaultRoles;
|
||||
@Deprecated
|
||||
protected Set<String> requiredCredentials;
|
||||
protected String passwordPolicy;
|
||||
protected List<UserRepresentation> users;
|
||||
@@ -53,14 +56,20 @@ public class RealmRepresentation {
|
||||
protected Map<String, String> browserSecurityHeaders;
|
||||
protected Map<String, String> smtpServer;
|
||||
protected List<UserFederationProviderRepresentation> userFederationProviders;
|
||||
protected List<UserFederationMapperRepresentation> userFederationMappers;
|
||||
protected String loginTheme;
|
||||
protected String accountTheme;
|
||||
protected String adminTheme;
|
||||
protected String emailTheme;
|
||||
|
||||
protected Boolean eventsEnabled;
|
||||
protected Long eventsExpiration;
|
||||
protected List<String> eventsListeners;
|
||||
protected List<String> enabledEventTypes;
|
||||
|
||||
protected Boolean adminEventsEnabled;
|
||||
protected Boolean adminEventsDetailsEnabled;
|
||||
|
||||
private List<IdentityProviderRepresentation> identityProviders;
|
||||
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
|
||||
private List<ProtocolMapperRepresentation> protocolMappers;
|
||||
@@ -68,6 +77,9 @@ public class RealmRepresentation {
|
||||
protected Boolean internationalizationEnabled;
|
||||
protected Set<String> supportedLocales;
|
||||
protected String defaultLocale;
|
||||
protected List<AuthenticationFlowRepresentation> authenticationFlows;
|
||||
protected List<AuthenticatorConfigRepresentation> authenticatorConfig;
|
||||
protected List<RequiredActionProviderRepresentation> requiredActions;
|
||||
|
||||
@Deprecated
|
||||
protected Boolean social;
|
||||
@@ -177,11 +189,11 @@ public class RealmRepresentation {
|
||||
scopeMappings.add(mapping);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Set<String> getRequiredCredentials() {
|
||||
return requiredCredentials;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setRequiredCredentials(Set<String> requiredCredentials) {
|
||||
this.requiredCredentials = requiredCredentials;
|
||||
}
|
||||
@@ -262,10 +274,6 @@ public class RealmRepresentation {
|
||||
return passwordCredentialGrantAllowed;
|
||||
}
|
||||
|
||||
public void setPasswordCredentialGrantAllowed(Boolean passwordCredentialGrantAllowed) {
|
||||
this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
|
||||
}
|
||||
|
||||
public Boolean isRegistrationAllowed() {
|
||||
return registrationAllowed;
|
||||
}
|
||||
@@ -322,6 +330,14 @@ public class RealmRepresentation {
|
||||
this.resetPasswordAllowed = resetPassword;
|
||||
}
|
||||
|
||||
public Boolean isEditUsernameAllowed() {
|
||||
return editUsernameAllowed;
|
||||
}
|
||||
|
||||
public void setEditUsernameAllowed(Boolean editUsernameAllowed) {
|
||||
this.editUsernameAllowed = editUsernameAllowed;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Boolean isSocial() {
|
||||
return social;
|
||||
@@ -507,6 +523,22 @@ public class RealmRepresentation {
|
||||
this.enabledEventTypes = enabledEventTypes;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsEnabled() {
|
||||
return adminEventsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsEnabled(Boolean adminEventsEnabled) {
|
||||
this.adminEventsEnabled = adminEventsEnabled;
|
||||
}
|
||||
|
||||
public Boolean isAdminEventsDetailsEnabled() {
|
||||
return adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public void setAdminEventsDetailsEnabled(Boolean adminEventsDetailsEnabled) {
|
||||
this.adminEventsDetailsEnabled = adminEventsDetailsEnabled;
|
||||
}
|
||||
|
||||
public List<UserFederationProviderRepresentation> getUserFederationProviders() {
|
||||
return userFederationProviders;
|
||||
}
|
||||
@@ -515,6 +547,19 @@ public class RealmRepresentation {
|
||||
this.userFederationProviders = userFederationProviders;
|
||||
}
|
||||
|
||||
public List<UserFederationMapperRepresentation> getUserFederationMappers() {
|
||||
return userFederationMappers;
|
||||
}
|
||||
|
||||
public void setUserFederationMappers(List<UserFederationMapperRepresentation> userFederationMappers) {
|
||||
this.userFederationMappers = userFederationMappers;
|
||||
}
|
||||
|
||||
public void addUserFederationMapper(UserFederationMapperRepresentation userFederationMapper) {
|
||||
if (userFederationMappers == null) userFederationMappers = new LinkedList<>();
|
||||
userFederationMappers.add(userFederationMapper);
|
||||
}
|
||||
|
||||
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
||||
return identityProviders;
|
||||
}
|
||||
@@ -585,5 +630,27 @@ public class RealmRepresentation {
|
||||
identityProviderMappers.add(rep);
|
||||
}
|
||||
|
||||
public List<AuthenticationFlowRepresentation> getAuthenticationFlows() {
|
||||
return authenticationFlows;
|
||||
}
|
||||
|
||||
public void setAuthenticationFlows(List<AuthenticationFlowRepresentation> authenticationFlows) {
|
||||
this.authenticationFlows = authenticationFlows;
|
||||
}
|
||||
|
||||
public List<AuthenticatorConfigRepresentation> getAuthenticatorConfig() {
|
||||
return authenticatorConfig;
|
||||
}
|
||||
|
||||
public void setAuthenticatorConfig(List<AuthenticatorConfigRepresentation> authenticatorConfig) {
|
||||
this.authenticatorConfig = authenticatorConfig;
|
||||
}
|
||||
|
||||
public List<RequiredActionProviderRepresentation> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(List<RequiredActionProviderRepresentation> requiredActions) {
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
}
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RequiredActionProviderRepresentation {
|
||||
|
||||
private String alias;
|
||||
private String name;
|
||||
private String providerId;
|
||||
private boolean enabled;
|
||||
private boolean defaultAction;
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for display purposes. Probably should clean this code up and make alias and name the same, but
|
||||
* the old code references an Enum and the admin console creates a "friendly" name for each enum.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isDefaultAction() {
|
||||
return defaultAction;
|
||||
}
|
||||
|
||||
public void setDefaultAction(boolean defaultAction) {
|
||||
this.defaultAction = defaultAction;
|
||||
}
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationMapperRepresentation {
|
||||
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String federationProviderDisplayName;
|
||||
protected String federationMapperType;
|
||||
protected Map<String, String> config;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFederationProviderDisplayName() {
|
||||
return federationProviderDisplayName;
|
||||
}
|
||||
|
||||
public void setFederationProviderDisplayName(String federationProviderDisplayName) {
|
||||
this.federationProviderDisplayName = federationProviderDisplayName;
|
||||
}
|
||||
|
||||
public String getFederationMapperType() {
|
||||
return federationMapperType;
|
||||
}
|
||||
|
||||
public void setFederationMapperType(String federationMapperType) {
|
||||
this.federationMapperType = federationMapperType;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserFederationMapperTypeRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String category;
|
||||
protected String helpText;
|
||||
|
||||
protected List<ConfigPropertyRepresentation> properties = new LinkedList<>();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return helpText;
|
||||
}
|
||||
|
||||
public void setHelpText(String helpText) {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
|
||||
public List<ConfigPropertyRepresentation> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(List<ConfigPropertyRepresentation> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
@@ -13,6 +17,7 @@ public class UserRepresentation {
|
||||
|
||||
protected String self; // link
|
||||
protected String id;
|
||||
protected Long createdTimestamp;
|
||||
protected String username;
|
||||
protected boolean enabled;
|
||||
protected boolean totp;
|
||||
@@ -21,7 +26,10 @@ public class UserRepresentation {
|
||||
protected String lastName;
|
||||
protected String email;
|
||||
protected String federationLink;
|
||||
protected Map<String, String> attributes;
|
||||
protected String serviceAccountClientId; // For rep, it points to clientId (not DB ID)
|
||||
|
||||
// Currently there is Map<String, List<String>> but for backwards compatibility, we also need to support Map<String, String>
|
||||
protected Map<String, Object> attributes;
|
||||
protected List<CredentialRepresentation> credentials;
|
||||
protected List<String> requiredActions;
|
||||
protected List<FederatedIdentityRepresentation> federatedIdentities;
|
||||
@@ -50,6 +58,14 @@ public class UserRepresentation {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
@@ -106,17 +122,23 @@ public class UserRepresentation {
|
||||
this.emailVerified = emailVerified;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
public Map<String, Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
// This method can be removed once we can remove backwards compatibility with Keycloak 1.3 (then getAttributes() can be changed to return Map<String, List<String>> )
|
||||
@JsonIgnore
|
||||
public Map<String, List<String>> getAttributesAsListValues() {
|
||||
return (Map) attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, Object> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public UserRepresentation attribute(String name, String value) {
|
||||
if (this.attributes == null) attributes = new HashMap<String, String>();
|
||||
attributes.put(name, value);
|
||||
public UserRepresentation singleAttribute(String name, String value) {
|
||||
if (this.attributes == null) attributes = new HashMap<>();
|
||||
attributes.put(name, Arrays.asList(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -197,4 +219,12 @@ public class UserRepresentation {
|
||||
public void setFederationLink(String federationLink) {
|
||||
this.federationLink = federationLink;
|
||||
}
|
||||
|
||||
public String getServiceAccountClientId() {
|
||||
return serviceAccountClientId;
|
||||
}
|
||||
|
||||
public void setServiceAccountClientId(String serviceAccountClientId) {
|
||||
this.serviceAccountClientId = serviceAccountClientId;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ import java.util.Set;
|
||||
*/
|
||||
public class UserSessionRepresentation {
|
||||
private String id;
|
||||
private String user;
|
||||
private String username;
|
||||
private String userId;
|
||||
private String ipAddress;
|
||||
private long start;
|
||||
private long lastAccess;
|
||||
@@ -25,12 +26,20 @@ public class UserSessionRepresentation {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
|
||||
Regular → Executable
+11
-1
@@ -34,7 +34,17 @@ public class HtmlUtils {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char chr = value.charAt(i);
|
||||
|
||||
if (chr != '\'' && chr != '"' && chr != '<' && chr != '>' && chr != '/') {
|
||||
if (chr == '<') {
|
||||
escaped.append("<");
|
||||
} else if (chr == '>') {
|
||||
escaped.append(">");
|
||||
} else if (chr == '"') {
|
||||
escaped.append(""");
|
||||
} else if (chr == '\'') {
|
||||
escaped.append("'");
|
||||
} else if (chr == '&') {
|
||||
escaped.append("&");
|
||||
} else {
|
||||
escaped.append(chr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.keycloak.util;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class SystemEnvProperties extends Properties {
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
if (key.startsWith("env.")) {
|
||||
return System.getenv().get(key.substring(4));
|
||||
} else {
|
||||
return System.getProperty(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
String value = getProperty(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.keycloak.util;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.io.IOContext;
|
||||
@@ -16,6 +18,8 @@ import org.codehaus.jackson.util.JsonParserDelegate;
|
||||
*/
|
||||
public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
|
||||
private static final Properties properties = new SystemEnvProperties();
|
||||
|
||||
@Override
|
||||
protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
|
||||
JsonParser delegate = super._createJsonParser(data, offset, len, ctxt);
|
||||
@@ -34,8 +38,6 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
return new SystemPropertiesAwareJsonParser(delegate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class SystemPropertiesAwareJsonParser extends JsonParserDelegate {
|
||||
|
||||
public SystemPropertiesAwareJsonParser(JsonParser d) {
|
||||
@@ -45,7 +47,7 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
@Override
|
||||
public String getText() throws IOException {
|
||||
String orig = super.getText();
|
||||
return StringPropertyReplacer.replaceProperties(orig);
|
||||
return StringPropertyReplacer.replaceProperties(orig, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonAnyGetter;
|
||||
@@ -90,4 +91,21 @@ public class JsonParserTest {
|
||||
Assert.assertEquals(100, config.getCorsMaxAge());
|
||||
Assert.assertEquals(200, config.getConnectionPoolSize());
|
||||
}
|
||||
|
||||
static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||
|
||||
@Test
|
||||
public void testSub() {
|
||||
String pattern = "${ALIAS}.${CRAP}";
|
||||
Matcher m = substitution.matcher(pattern);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
System.out.println("GROUP: " + m.group(1));
|
||||
m.appendReplacement(sb, m.group(1));
|
||||
|
||||
}
|
||||
m.appendTail(sb);
|
||||
System.out.println(sb.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -96,6 +96,7 @@ public class RSAVerifierTest {
|
||||
String encoded = new JWSBuilder()
|
||||
.jsonContent(token)
|
||||
.rsa256(idpPair.getPrivate());
|
||||
System.out.print("encoded size: " + encoded.length());
|
||||
AccessToken token = verifySkeletonKeyToken(encoded);
|
||||
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
|
||||
Assert.assertEquals("CN=Client", token.getSubject());
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.jose;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.HMACProvider;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HmacTest {
|
||||
|
||||
@Test
|
||||
public void testHmacSignatures() throws Exception {
|
||||
SecretKey secret = new SecretKeySpec(UUID.randomUUID().toString().getBytes(), "HmacSHA256");
|
||||
String encoded = new JWSBuilder().content("hello world".getBytes())
|
||||
.hmac256(secret);
|
||||
JWSInput input = new JWSInput(encoded);
|
||||
Assert.assertTrue(HMACProvider.verify(input, secret));
|
||||
}
|
||||
}
|
||||
Loaded 100 of 2006 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user