Compare commits
827
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965c44a43d | ||
|
|
905061e2b7 | ||
|
|
45df3087f2 | ||
|
|
f9f53a3355 | ||
|
|
ab08ac8ad4 | ||
|
|
12c9475bfc | ||
|
|
a0d7fc12db | ||
|
|
5b5fea347a | ||
|
|
d049007412 | ||
|
|
cc14a3b288 | ||
|
|
bcd3eeda49 | ||
|
|
992b365eab | ||
|
|
3882cc4aa8 | ||
|
|
38bd79ab88 | ||
|
|
790fd1d94d | ||
|
|
19e1ddff1d | ||
|
|
0bbc26bd0b | ||
|
|
1f3db436d8 | ||
|
|
7ac663bd5f | ||
|
|
53fbceb49a | ||
|
|
1159dc091d | ||
|
|
38c0b4ad65 | ||
|
|
f4f563d570 | ||
|
|
550ba16a09 | ||
|
|
0289f5dff9 | ||
|
|
ea0ceeb65d | ||
|
|
3746bf28a1 | ||
|
|
a497c69758 | ||
|
|
731d75c756 | ||
|
|
982ba5ab90 | ||
|
|
c2e2a46025 | ||
|
|
8430fb0ed3 | ||
|
|
9a829a38b1 | ||
|
|
04ec750157 | ||
|
|
3655db2a30 | ||
|
|
5edb84ccfb | ||
|
|
95e2da9c9e | ||
|
|
6cb1eb41cf | ||
|
|
46a0caf2e0 | ||
|
|
d96b660686 | ||
|
|
ae66b1b88e | ||
|
|
60a564dcfd | ||
|
|
69656d2775 | ||
|
|
24415f2186 | ||
|
|
0ae3eafe10 | ||
|
|
6cf708f6e1 | ||
|
|
f5a626006c | ||
|
|
101a72cd65 | ||
|
|
312429cb88 | ||
|
|
c9851f1c05 | ||
|
|
73b7fe6169 | ||
|
|
d1c5c55a61 | ||
|
|
bdc31d9459 | ||
|
|
f08c42a37e | ||
|
|
8fbd39934f | ||
|
|
c460630cf8 | ||
|
|
335bc87ad8 | ||
|
|
6cbeeb1b1a | ||
|
|
47c9f1439d | ||
|
|
64589bc807 | ||
|
|
2eae9c17a4 | ||
|
|
9f6c206078 | ||
|
|
2460eecd75 | ||
|
|
a084695978 | ||
|
|
705da88daa | ||
|
|
ce85a42ecf | ||
|
|
2990b75faa | ||
|
|
94c06f3f5d | ||
|
|
b72a7db6fe | ||
|
|
b960f18f4e | ||
|
|
d0dc75c06f | ||
|
|
13a5eccaf8 | ||
|
|
898195e1da | ||
|
|
8ea0d19d2f | ||
|
|
465318b352 | ||
|
|
fa01d53f9e | ||
|
|
ba8fe1ddaf | ||
|
|
50bc53d673 | ||
|
|
d682701232 | ||
|
|
544883ca9f | ||
|
|
54a7761e15 | ||
|
|
ed51b4e1ea | ||
|
|
fcc4781e3a | ||
|
|
24b5bd6862 | ||
|
|
614c718304 | ||
|
|
e2ce33a86a | ||
|
|
e338f1af15 | ||
|
|
eefc5ed1d7 | ||
|
|
7694b130e6 | ||
|
|
32ad7271f3 | ||
|
|
2b012ff238 | ||
|
|
1bd219ed40 | ||
|
|
02a59812e6 | ||
|
|
7f4849e165 | ||
|
|
628c7dd92a | ||
|
|
bace16bac6 | ||
|
|
3bb0842eae | ||
|
|
a6b38397ea | ||
|
|
c63467c79f | ||
|
|
b1cd37814a | ||
|
|
07d10ee583 | ||
|
|
614bee3061 | ||
|
|
81dfea4052 | ||
|
|
1b14444eba | ||
|
|
245d83adce | ||
|
|
b5835fc232 | ||
|
|
8a54f133ba | ||
|
|
511cc483cb | ||
|
|
6e18a16f78 | ||
|
|
43d08f2d05 | ||
|
|
171e9b1ce9 | ||
|
|
eec582e704 | ||
|
|
4b05feb6d6 | ||
|
|
d6f5bf6cde | ||
|
|
f1d72d0b6d | ||
|
|
148d494905 | ||
|
|
38b575d91b | ||
|
|
baf99a2280 | ||
|
|
1d80b87bcc | ||
|
|
84754c47d3 | ||
|
|
64b8a990f9 | ||
|
|
0580c2223b | ||
|
|
824aed773b | ||
|
|
9164e69d49 | ||
|
|
670dc39d4f | ||
|
|
b196d0dded | ||
|
|
bfd62b0807 | ||
|
|
3514d5cd78 | ||
|
|
b3b46339a4 | ||
|
|
9ff4947abd | ||
|
|
469eb77486 | ||
|
|
213b685b43 | ||
|
|
42df327104 | ||
|
|
d2b42e3843 | ||
|
|
b999c1e5b1 | ||
|
|
2ecf696a12 | ||
|
|
063a63445a | ||
|
|
b84bdcf464 | ||
|
|
f275ecf6d5 | ||
|
|
b241920802 | ||
|
|
dcd1406a27 | ||
|
|
d2d408e223 | ||
|
|
9699d8aa82 | ||
|
|
6d8d9107c6 | ||
|
|
fb8483dc56 | ||
|
|
85621bdd9f | ||
|
|
573f7fc1fb | ||
|
|
14af840c3a | ||
|
|
aa2d1bcc93 | ||
|
|
20cde9cd00 | ||
|
|
3dff4afe83 | ||
|
|
3edce7f580 | ||
|
|
35d601d795 | ||
|
|
f00957ec3a | ||
|
|
46c04561ff | ||
|
|
9abe357754 | ||
|
|
3c99350247 | ||
|
|
b041f2a857 | ||
|
|
0a15f873af | ||
|
|
08b0aebe03 | ||
|
|
a323fe7992 | ||
|
|
1963e94e33 | ||
|
|
7437af0131 | ||
|
|
60a1fc3020 | ||
|
|
8714193c97 | ||
|
|
a61733cf47 | ||
|
|
1aff8af336 | ||
|
|
c21be01e97 | ||
|
|
72d240a57e | ||
|
|
83769b3d52 | ||
|
|
1a5c2365a1 | ||
|
|
87e40f1820 | ||
|
|
8fbfba907b | ||
|
|
f342a8c7a3 | ||
|
|
866715b752 | ||
|
|
e0ec121012 | ||
|
|
63ff5736a2 | ||
|
|
3451778f48 | ||
|
|
e4b3007ce2 | ||
|
|
a5593469ae | ||
|
|
484a4c1454 | ||
|
|
09a336c708 | ||
|
|
4ac5fb08e3 | ||
|
|
ff86bdc35f | ||
|
|
726ce08700 | ||
|
|
c14840a4c0 | ||
|
|
24a492ffe0 | ||
|
|
c4ff4e030a | ||
|
|
7244944879 | ||
|
|
1f2db2c049 | ||
|
|
d6e8b1ba2c | ||
|
|
7f0a3eebfb | ||
|
|
5d0345c922 | ||
|
|
99a041158c | ||
|
|
8f7efd5b67 | ||
|
|
d2c27daa16 | ||
|
|
2d2041db5e | ||
|
|
e3f260c57f | ||
|
|
8113c3f05e | ||
|
|
e3bc15a4d9 | ||
|
|
3f68180ee7 | ||
|
|
6bb6dc0aaf | ||
|
|
d625fb014c | ||
|
|
8cc7676335 | ||
|
|
626cecba95 | ||
|
|
65348cd5e7 | ||
|
|
5f5316fbdc | ||
|
|
011bac7a1e | ||
|
|
86671a2354 | ||
|
|
8fac39fffa | ||
|
|
a2710e01da | ||
|
|
38abcd4a90 | ||
|
|
62dd6fbc6b | ||
|
|
75c56a2a7c | ||
|
|
7491b1c369 | ||
|
|
a3cca7424a | ||
|
|
387aed6269 | ||
|
|
20bf888d73 | ||
|
|
ad6d52cb0f | ||
|
|
8dc293f629 | ||
|
|
de8819aae3 | ||
|
|
e98ced5d0f | ||
|
|
6775af302a | ||
|
|
621cae61b6 | ||
|
|
922a8f7eef | ||
|
|
48a0e564b9 | ||
|
|
0758d89a59 | ||
|
|
e94b6acd02 | ||
|
|
a1beff7d3d | ||
|
|
ffc30b4c03 | ||
|
|
6d824231e9 | ||
|
|
060a90197e | ||
|
|
df7aac01dd | ||
|
|
69b00d859b | ||
|
|
9c88979040 | ||
|
|
bda218af07 | ||
|
|
15d7568792 | ||
|
|
18b14b54f4 | ||
|
|
38857cf2e6 | ||
|
|
0647590170 | ||
|
|
099743d448 | ||
|
|
4cbc423d67 | ||
|
|
e37b5613ed | ||
|
|
f92ad89a6c | ||
|
|
e99a675c50 | ||
|
|
4593bff76f | ||
|
|
81d6590544 | ||
|
|
2f9597cf51 | ||
|
|
8a246a6444 | ||
|
|
876d493458 | ||
|
|
6f023fcae4 | ||
|
|
0abd93417a | ||
|
|
b4f5fe9977 | ||
|
|
e15b0c7d90 | ||
|
|
3cf52b018c | ||
|
|
cb9f159091 | ||
|
|
64cea5f25a | ||
|
|
b6fd58e644 | ||
|
|
de774a3058 | ||
|
|
9ebe2ff45e | ||
|
|
4ebc575afc | ||
|
|
a1d4d8e13f | ||
|
|
69f7dfbb47 | ||
|
|
99ce0a36e6 | ||
|
|
67c34554db | ||
|
|
9ba61af83b | ||
|
|
436f5f6229 | ||
|
|
270d6108ca | ||
|
|
18094370a6 | ||
|
|
26eac398e0 | ||
|
|
93c896b4f7 | ||
|
|
6b29202f1f | ||
|
|
3b1d8fd8fe | ||
|
|
630d62f014 | ||
|
|
711b8ca747 | ||
|
|
34a1923d18 | ||
|
|
6291936ce9 | ||
|
|
5220c6c288 | ||
|
|
532c82f752 | ||
|
|
61218715af | ||
|
|
8dce5a90bb | ||
|
|
8d794b8151 | ||
|
|
b1cfab34fd | ||
|
|
159a5e20bf | ||
|
|
aea4fd8e30 | ||
|
|
767e6a9783 | ||
|
|
bb8ac0000e | ||
|
|
d73d2e0788 | ||
|
|
714e83198e | ||
|
|
c780918501 | ||
|
|
8bcb01fadb | ||
|
|
f78f5366cf | ||
|
|
f0d02f24b1 | ||
|
|
15bc33f8e6 | ||
|
|
d21a19925b | ||
|
|
a1e9d99d3e | ||
|
|
094cf675c7 | ||
|
|
efcb7ee110 | ||
|
|
c8321fc96e | ||
|
|
56ea29e899 | ||
|
|
df2305eb02 | ||
|
|
5281793078 | ||
|
|
9beb210c23 | ||
|
|
d1c890db18 | ||
|
|
0be4a4b71d | ||
|
|
19a545049b | ||
|
|
34eb34c5fc | ||
|
|
28adc28a56 | ||
|
|
5d8c803da6 | ||
|
|
2fd7fdaa74 | ||
|
|
f9cb99a1ee | ||
|
|
37bfb2c594 | ||
|
|
3b55248e6c | ||
|
|
b35bccdad2 | ||
|
|
6cabf3e57d | ||
|
|
b31b54e17c | ||
|
|
469d044254 | ||
|
|
9a26d7078f | ||
|
|
896c32a660 | ||
|
|
6e23613b9e | ||
|
|
d15324110e | ||
|
|
153de54fe8 | ||
|
|
2866f6fc23 | ||
|
|
9228f0c076 | ||
|
|
1eafb01c8d | ||
|
|
44f446e745 | ||
|
|
c71fdc72c7 | ||
|
|
bbf944a225 | ||
|
|
dbc5641144 | ||
|
|
9080a64fcd | ||
|
|
f50177996d | ||
|
|
b649dd39d9 | ||
|
|
ab87c03732 | ||
|
|
8eaf77b3c1 | ||
|
|
cbea141a54 | ||
|
|
60a040a141 | ||
|
|
4a518c964b | ||
|
|
bb2ad656a3 | ||
|
|
30b95e0a57 | ||
|
|
852c631eff | ||
|
|
211a279248 | ||
|
|
2ae2067841 | ||
|
|
0dd06e3343 | ||
|
|
4e9bec69e4 | ||
|
|
70392d9f1a | ||
|
|
9a3abf6a6a | ||
|
|
0bfcbdb359 | ||
|
|
2f3c8bf079 | ||
|
|
e7bde986aa | ||
|
|
f3211bf34d | ||
|
|
c086e0a502 | ||
|
|
80f4b668b2 | ||
|
|
01606ab5e0 | ||
|
|
91e29d9710 | ||
|
|
23d101f8fa | ||
|
|
e08b3e7dec | ||
|
|
c4e32b7c45 | ||
|
|
14ece40054 | ||
|
|
eaa6be55d7 | ||
|
|
2ae714aea2 | ||
|
|
5f6687ec62 | ||
|
|
06053fad85 | ||
|
|
d0c06ab0ce | ||
|
|
cfb6f927b2 | ||
|
|
8d33894989 | ||
|
|
ba13cbe004 | ||
|
|
9410adf9ce | ||
|
|
7443c78087 | ||
|
|
d5b34a0b41 | ||
|
|
0cd64e297a | ||
|
|
0fcf80dd6e | ||
|
|
56e9e0b4f9 | ||
|
|
dfcd3267e4 | ||
|
|
f689747fd6 | ||
|
|
bb11165568 | ||
|
|
a180c6b896 | ||
|
|
34d3be244d | ||
|
|
4662781213 | ||
|
|
5bd12a708f | ||
|
|
e39149c954 | ||
|
|
b925a97b5d | ||
|
|
3bcf2c81da | ||
|
|
b52801de25 | ||
|
|
4d8c3d7d73 | ||
|
|
ea1f10361a | ||
|
|
5dafad71ac | ||
|
|
df28fb97ed | ||
|
|
5600018a56 | ||
|
|
441c00c06f | ||
|
|
9533bbc47d | ||
|
|
2025d314ec | ||
|
|
772f991545 | ||
|
|
f6b59b03bb | ||
|
|
2ae3775841 | ||
|
|
8573c13b8c | ||
|
|
db1c6e9afd | ||
|
|
429f003578 | ||
|
|
dd684858f2 | ||
|
|
2e82d56634 | ||
|
|
1ede6e70b7 | ||
|
|
8908fbef58 | ||
|
|
09a96b3a2b | ||
|
|
5b7c8b6920 | ||
|
|
409b46963e | ||
|
|
53cf574735 | ||
|
|
fe236cabd3 | ||
|
|
d25536569a | ||
|
|
06b9499805 | ||
|
|
2a2e57467d | ||
|
|
24ac6cbbac | ||
|
|
0b68e52b83 | ||
|
|
cf1393ffe1 | ||
|
|
c4808a3e61 | ||
|
|
f0d37b19e2 | ||
|
|
65206a72b2 | ||
|
|
a91d4c06ee | ||
|
|
e8ad08bd28 | ||
|
|
7a3b1ddd18 | ||
|
|
db939ee076 | ||
|
|
5ede17d8b8 | ||
|
|
0ff992e04e | ||
|
|
34a579821a | ||
|
|
dd30a35e1e | ||
|
|
2074e1423f | ||
|
|
d5476b78ec | ||
|
|
df19700f69 | ||
|
|
3c605a7740 | ||
|
|
a64b35d72f | ||
|
|
e880b77692 | ||
|
|
e8fe26ff1c | ||
|
|
108ccd98d0 | ||
|
|
5fddc28cd6 | ||
|
|
63001d5e16 | ||
|
|
caccf76ea4 | ||
|
|
5120549005 | ||
|
|
e67032a9d7 | ||
|
|
a903eb7605 | ||
|
|
5b431338c2 | ||
|
|
c20d488648 | ||
|
|
57a8f3f5ee | ||
|
|
6dda7fc356 | ||
|
|
9261c1608f | ||
|
|
80cd43390b | ||
|
|
c196849574 | ||
|
|
1007d19860 | ||
|
|
374a7b8848 | ||
|
|
8ae57fd117 | ||
|
|
5a2fd16b92 | ||
|
|
3545ac130b | ||
|
|
199c786e22 | ||
|
|
38b52283a5 | ||
|
|
1f8b2d4c7b | ||
|
|
9c0fa93510 | ||
|
|
ab2932f75e | ||
|
|
81083ef470 | ||
|
|
e4232c73c6 | ||
|
|
29070cec77 | ||
|
|
fbef9f7691 | ||
|
|
97f3a3feef | ||
|
|
54cc095ff5 | ||
|
|
0ad4d5cc47 | ||
|
|
236f3ab768 | ||
|
|
6de7c42ac9 | ||
|
|
bb7a809ab5 | ||
|
|
142b155460 | ||
|
|
0f6bc9c5c7 | ||
|
|
e600d7c449 | ||
|
|
37327be521 | ||
|
|
07d2bf988a | ||
|
|
df06a0c878 | ||
|
|
cee3fea98f | ||
|
|
5c378cbd9d | ||
|
|
37099f3177 | ||
|
|
128cc7eadb | ||
|
|
de36973716 | ||
|
|
08b1996149 | ||
|
|
0574302840 | ||
|
|
fad0a7728c | ||
|
|
86ad7292af | ||
|
|
74e2bfd9bd | ||
|
|
0c60bd80df | ||
|
|
46f81a331a | ||
|
|
b0ba7ed188 | ||
|
|
0d9e14d4c7 | ||
|
|
10ae457237 | ||
|
|
73f59417bd | ||
|
|
6814230193 | ||
|
|
d68131ac00 | ||
|
|
952f098440 | ||
|
|
f08477ea66 | ||
|
|
8544dd66d8 | ||
|
|
a5a2848251 | ||
|
|
7232535729 | ||
|
|
271292dbd4 | ||
|
|
eb47d43497 | ||
|
|
fd9317a295 | ||
|
|
11fc6d5321 | ||
|
|
64a36aee03 | ||
|
|
59440840fe | ||
|
|
c2075af7f7 | ||
|
|
ab85f09608 | ||
|
|
da799b18c3 | ||
|
|
ca6de3e486 | ||
|
|
ae3c4e8459 | ||
|
|
8e79f550b0 | ||
|
|
0137918701 | ||
|
|
79828ddc78 | ||
|
|
fcca9dd90a | ||
|
|
1b4b537043 | ||
|
|
b09e2f697e | ||
|
|
2855baecef | ||
|
|
b7cf1bb215 | ||
|
|
b3dd349342 | ||
|
|
357337047b | ||
|
|
5496f043a5 | ||
|
|
a22aa8055f | ||
|
|
57f0ea0638 | ||
|
|
f723ac79d1 | ||
|
|
3a15b416a9 | ||
|
|
cc97265c06 | ||
|
|
665038d0ce | ||
|
|
8d1149dc07 | ||
|
|
ebea9e9270 | ||
|
|
b5aa9efefb | ||
|
|
95f56e2e62 | ||
|
|
273775d848 | ||
|
|
8606f344a8 | ||
|
|
53c3696cbb | ||
|
|
7203d9ba12 | ||
|
|
26d3e48555 | ||
|
|
a53206995a | ||
|
|
b2c8f48af9 | ||
|
|
11c23a7945 | ||
|
|
3be4579204 | ||
|
|
a3d08e7191 | ||
|
|
7e8b16f975 | ||
|
|
966eb2fe47 | ||
|
|
5826f02ebe | ||
|
|
2b525c6016 | ||
|
|
dc7923c436 | ||
|
|
00a64eeb89 | ||
|
|
76c9f2c4ec | ||
|
|
dafa52ea93 | ||
|
|
c2bf6c3822 | ||
|
|
ab8de6ba25 | ||
|
|
6714e2b87f | ||
|
|
855269f35d | ||
|
|
a3ed02ea16 | ||
|
|
84f13eadc1 | ||
|
|
cedf43c084 | ||
|
|
4c281a39bb | ||
|
|
5533357c46 | ||
|
|
ae4d83107a | ||
|
|
b92494f023 | ||
|
|
d3a2617d3c | ||
|
|
740943290c | ||
|
|
c51c7fe189 | ||
|
|
610eaaaaff | ||
|
|
bde9944449 | ||
|
|
1318ea85be | ||
|
|
67e3e60f28 | ||
|
|
bc2360e985 | ||
|
|
8e7b0022a6 | ||
|
|
27efd3c0a4 | ||
|
|
07a31d3f35 | ||
|
|
07dbb7d7b7 | ||
|
|
26f6da10dd | ||
|
|
7ba8e09aef | ||
|
|
d95372c993 | ||
|
|
643daadf60 | ||
|
|
7bea4af6c9 | ||
|
|
b27776b3c6 | ||
|
|
d9c8dedcfb | ||
|
|
9e0bf9e7dc | ||
|
|
0e0dfb60e0 | ||
|
|
b95fd0d0b0 | ||
|
|
f4f9b1e323 | ||
|
|
f7c3373f75 | ||
|
|
639b7c5610 | ||
|
|
b30809d681 | ||
|
|
0731f9977b | ||
|
|
e8f8829a76 | ||
|
|
663ff30eae | ||
|
|
01d22c3aab | ||
|
|
9b24369b92 | ||
|
|
f6645eea92 | ||
|
|
634f61281d | ||
|
|
c560a739b1 | ||
|
|
ea672e36ae | ||
|
|
eb9335f706 | ||
|
|
d429525620 | ||
|
|
b1a480c20d | ||
|
|
0fcebb8670 | ||
|
|
3e32bd116b | ||
|
|
a4cb268703 | ||
|
|
06b98589eb | ||
|
|
1e1991e285 | ||
|
|
b0446fab0c | ||
|
|
1194e40ef2 | ||
|
|
2a820c73b1 | ||
|
|
624fcf6197 | ||
|
|
59747500aa | ||
|
|
6f0b12174c | ||
|
|
5568c8dc7d | ||
|
|
d957cc4883 | ||
|
|
2d949b63b5 | ||
|
|
abdbef6a51 | ||
|
|
c51011acef | ||
|
|
6b1e0401ba | ||
|
|
d523242dbb | ||
|
|
92058b16c9 | ||
|
|
4424c425a6 | ||
|
|
5f7b0aae06 | ||
|
|
f77e3b5532 | ||
|
|
334b981741 | ||
|
|
957ff7d206 | ||
|
|
5973ccd522 | ||
|
|
1ea1f0af96 | ||
|
|
f5ac81db41 | ||
|
|
9e7f52ccda | ||
|
|
f8173e6a5a | ||
|
|
5edf05d569 | ||
|
|
4666bcbfb2 | ||
|
|
a374e52ea5 | ||
|
|
f987d331b7 | ||
|
|
314f177fe7 | ||
|
|
64f591769a | ||
|
|
0801c9c120 | ||
|
|
e4e02a2615 | ||
|
|
ff69607f52 | ||
|
|
a23b213736 | ||
|
|
36ce4e04d9 | ||
|
|
3b6593b476 | ||
|
|
6ef3102cd1 | ||
|
|
39381c104c | ||
|
|
c04b490f29 | ||
|
|
e1f2d296bf | ||
|
|
1d760388de | ||
|
|
8286d1ce34 | ||
|
|
7b14d29dee | ||
|
|
75224d25b0 | ||
|
|
3a3ae742a4 | ||
|
|
e7c64b7812 | ||
|
|
e55eb73fb9 | ||
|
|
201b35be54 | ||
|
|
e25b2b05ae | ||
|
|
6b470f826e | ||
|
|
4517d9d707 | ||
|
|
7ff2c77a82 | ||
|
|
e5e43173bc | ||
|
|
6f3822e6ea | ||
|
|
95bafc416d | ||
|
|
2576975988 | ||
|
|
8c5ae9d052 | ||
|
|
8ed5282da5 | ||
|
|
9b6d9fda43 | ||
|
|
77c6b391c4 | ||
|
|
c8432f74f6 | ||
|
|
8fa3425886 | ||
|
|
84396d0da1 | ||
|
|
38e6bde07e | ||
|
|
9811aaeecc | ||
|
|
7284aae4df | ||
|
|
316431c4d1 | ||
|
|
bb039576d4 | ||
|
|
646e762cbe | ||
|
|
1d94649b96 | ||
|
|
1fa019e9dd | ||
|
|
364998b34b | ||
|
|
6778fbfa3b | ||
|
|
bb11ad2ec1 | ||
|
|
b6386630fb | ||
|
|
363e5cdf64 | ||
|
|
7594be8014 | ||
|
|
62f8811a46 | ||
|
|
2d1dc4a874 | ||
|
|
0546aebc84 | ||
|
|
c06009d5fb | ||
|
|
3d02fd5d0e | ||
|
|
562f6703b6 | ||
|
|
573c88564b | ||
|
|
dabf101d96 | ||
|
|
292089cee8 | ||
|
|
5594c73ceb | ||
|
|
ce6652efb1 | ||
|
|
f64f55a4c6 | ||
|
|
9a3a8c39f4 | ||
|
|
1de4c31750 | ||
|
|
bda85f700a | ||
|
|
3471ce6c63 | ||
|
|
b8dfac391a | ||
|
|
28546ad031 | ||
|
|
d6e5e376bf | ||
|
|
9b055c6ad6 | ||
|
|
a3c17d1071 | ||
|
|
d57cd1aa91 | ||
|
|
3405330d92 | ||
|
|
541e865fe8 | ||
|
|
c03b97d2d4 | ||
|
|
5d275de8b2 | ||
|
|
80f3a3152c | ||
|
|
54abfb670c | ||
|
|
b41bf9f231 | ||
|
|
2b8d2288fb | ||
|
|
b556ff8a83 | ||
|
|
4c8471ef43 | ||
|
|
1aa58ec580 | ||
|
|
272806a3f3 | ||
|
|
51a718f337 | ||
|
|
cc982cf246 | ||
|
|
0896808625 | ||
|
|
c562e88e75 | ||
|
|
abd7163205 | ||
|
|
1701afb6d4 | ||
|
|
d8937b11cf | ||
|
|
de871585e0 | ||
|
|
b41c45d40a | ||
|
|
ecaa0d1401 | ||
|
|
cdc35d99bc | ||
|
|
f5f96c5021 | ||
|
|
29a139f56b | ||
|
|
daf1f2ed45 | ||
|
|
eb37890924 | ||
|
|
eb3e69d5d7 | ||
|
|
5aefe52ccc | ||
|
|
0d00db281a | ||
|
|
c8c4cfbaae | ||
|
|
d5be56fa21 | ||
|
|
1d55a4f1e9 | ||
|
|
83bc115b63 | ||
|
|
e952ff142d | ||
|
|
b94bad7f58 | ||
|
|
354cf02aaa | ||
|
|
7f0cf3eda8 | ||
|
|
9bc0bd452d | ||
|
|
d124f43423 | ||
|
|
a197bda363 | ||
|
|
8ca46fa35d | ||
|
|
aec8feab49 | ||
|
|
e7c30fff48 | ||
|
|
2916cce6fb | ||
|
|
bf95c2ba85 | ||
|
|
6081572179 | ||
|
|
bbf5fee320 | ||
|
|
d66862fd26 | ||
|
|
1c0ef4621e | ||
|
|
323e032287 | ||
|
|
948960f33f | ||
|
|
abde247e14 | ||
|
|
a0cd2bf5a7 | ||
|
|
58083fbb96 | ||
|
|
ad068a300b | ||
|
|
be320e11a5 | ||
|
|
b7826be103 | ||
|
|
216e24864a | ||
|
|
afea032322 | ||
|
|
88ddc8ebca | ||
|
|
2aac603fad | ||
|
|
8caf3fa83a | ||
|
|
42975f0edf | ||
|
|
3433227fa7 | ||
|
|
e6067c915d | ||
|
|
fcfbf156ce | ||
|
|
d564955cab | ||
|
|
30d523a5c2 | ||
|
|
126b444d77 | ||
|
|
d58870545f | ||
|
|
231e8895a5 | ||
|
|
7d7b9e5b73 | ||
|
|
25bf6d63b4 | ||
|
|
05cd8a82e5 | ||
|
|
1c218c8e9b | ||
|
|
8af81cd290 | ||
|
|
869601d041 | ||
|
|
21239fbab9 | ||
|
|
722a077de7 | ||
|
|
1e15e97d17 | ||
|
|
225307e855 | ||
|
|
84f1e1b56e | ||
|
|
bdf1c1f495 | ||
|
|
5e1da96177 | ||
|
|
3ddb2084cd | ||
|
|
d831e25366 | ||
|
|
37fdc50fb4 | ||
|
|
d8a3025ea1 | ||
|
|
911cf2ae45 | ||
|
|
60ba41e82f | ||
|
|
129eb6a3be | ||
|
|
34fbdf4358 | ||
|
|
51b3de7383 | ||
|
|
0ec19c9ec9 | ||
|
|
4711dd083f | ||
|
|
bcc0bcd219 | ||
|
|
00aaefb926 | ||
|
|
3302c54c43 | ||
|
|
434dec2318 | ||
|
|
96a05a9b77 | ||
|
|
77a44751a6 | ||
|
|
51b95b1b91 | ||
|
|
ab02dea902 | ||
|
|
28a1bd8535 | ||
|
|
a2a8e72e20 | ||
|
|
4788dd8bd2 | ||
|
|
793f69d4b6 | ||
|
|
0879013a90 | ||
|
|
877a552f3b | ||
|
|
2403e1f465 | ||
|
|
119423895d | ||
|
|
702ae0307e | ||
|
|
b5bdeae2a2 | ||
|
|
4159a97881 | ||
|
|
2d5e6520db | ||
|
|
331ab71427 | ||
|
|
f9aaa16cfe | ||
|
|
2ebc32793a | ||
|
|
cec948ffb4 | ||
|
|
466b6762d9 | ||
|
|
e03737c665 | ||
|
|
3f1b7a0335 | ||
|
|
f26f2faa72 | ||
|
|
7763da1273 | ||
|
|
bb6791d6eb | ||
|
|
1c6b867079 | ||
|
|
6b57684c52 | ||
|
|
08769a2daa | ||
|
|
7fff2588b2 | ||
|
|
ee5cab04e0 |
No files matched your search
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-alpha-3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-admin-ui-styles</artifactId>
|
||||
<name>Keycloak Admin UI Styles</name>
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,71 +0,0 @@
|
||||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* Clearfix */
|
||||
|
||||
.clearfix:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
line-height: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
html[xmlns] .clearfix {
|
||||
display: block;
|
||||
}
|
||||
|
||||
* html .clearfix {
|
||||
height: 1%;
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="23px" height="14px" viewBox="0 0 23 14" enable-background="new 0 0 23 14" xml:space="preserve">
|
||||
<polyline fill="none" stroke="#009AD8" stroke-miterlimit="10" points="1,13 7,7 1,1 "/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 573 B |
-8
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="22px" height="52px" viewBox="0 0 22 52" enable-background="new 0 0 22 52" xml:space="preserve">
|
||||
<polygon fill="#828487" points="11,16 6.818,11.818 7.525,11.111 11,14.586 14.475,11.111 15.182,11.818 "/>
|
||||
<polygon fill="#DBDADA" points="11,42 6.818,37.818 7.525,37.111 11,40.586 14.475,37.111 15.182,37.818 "/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 699 B |
-1328
File diff suppressed because it is too large.
Load diff
|
Before Width: | Height: | Size: 250 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 269 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 268 KiB |
-9168
File diff suppressed because it is too large.
Load diff
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd ">
|
||||
</web-fragment>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-alpha-3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-admin-ui</artifactId>
|
||||
<name>Keycloak Admin UI</name>
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,192 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<style>
|
||||
.mock_view {
|
||||
padding: 2em 2em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<title>Keycloak Admin Console</title>
|
||||
|
||||
<link rel="icon" href="/auth/admin-ui/img/favicon.ico">
|
||||
|
||||
<!-- Frameworks -->
|
||||
<!--link rel="stylesheet" href="/auth/admin-ui/bootstrap/css/bootstrap.css"-->
|
||||
<link rel="stylesheet" href="/auth/admin-ui/patternfly/css/patternfly.css">
|
||||
|
||||
<!-- Page styles -->
|
||||
<link rel="stylesheet" href="/auth/admin-ui/css/admin-console.css">
|
||||
<link href="lib/select2-3.4.1/select2.css" rel="stylesheet">
|
||||
|
||||
<script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
|
||||
<script src="lib/select2-3.4.1/select2.js" type="text/javascript"></script>
|
||||
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="lib/angular/angular-resource.js"></script>
|
||||
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
|
||||
|
||||
<script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
|
||||
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
|
||||
<script src="lib/angular/select2.js" type="text/javascript"></script>
|
||||
<script src="lib/fileupload/angular-file-upload.min.js"></script> <!--
|
||||
<script src="/auth/admin-ui/bootstrap-3.0.0-wip/js/dropdown.js"></script>
|
||||
<script src="/auth/admin-ui/bootstrap-3.0.0-wip/js/tooltip.js"></script>
|
||||
-->
|
||||
<script src="/auth/admin-ui/js/bootstrap-tokenfield.js"></script>
|
||||
|
||||
<script src="/auth/admin-ui/bootstrap/js/bootstrap.js"></script>
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/controllers/realm.js"></script>
|
||||
<script src="js/controllers/applications.js"></script>
|
||||
<script src="js/controllers/oauth-clients.js"></script>
|
||||
<script src="js/controllers/users.js"></script>
|
||||
<script src="js/loaders.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
|
||||
<style>
|
||||
[ng\:cloak], [ng-cloak], .ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="admin-console" data-ng-controller="GlobalCtrl" ng-cloak>
|
||||
<div id="idletimeout">
|
||||
You will be logged off in <strong><span></span> seconds</strong> due to inactivity.
|
||||
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
|
||||
</div>
|
||||
|
||||
|
||||
<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
|
||||
<div class="alert alert-{{notification.type}}"><strong>{{notification.header}}</strong> {{notification.message}}</div>
|
||||
</div>
|
||||
|
||||
<header class="navbar navbar-default navbar-pf navbar-main header">
|
||||
<nav id="kc-navigation" class="navbar" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<div class="container">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<h1 class="navbar-title"><Strong>KeyCloak</Strong> Central Login</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav navbar-utility" data-ng-show="auth.loggedIn">
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
|
||||
<span class="pficon pficon-user"></span>
|
||||
{{auth.user.displayName}}<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/auth/rest/realms/keycloak-admin/account?referrer=admin-console">Manage Account</a></li>
|
||||
<li class="separator"><a href="/auth/rest/admin/logout">Sign Out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-primary persistent-secondary" data-ng-controller="RealmDropdownCtrl">
|
||||
<li class="dropdown context" data-ng-show="showNav()">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
Realm: {{current.realm.id}}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li data-ng-repeat="realm in current.realms">
|
||||
<a href="" ng-click="changeRealm(realm.id)" >{{realm.id}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
<!--------------------------------------->
|
||||
<li class="active">
|
||||
<a href="#">Settings</a>
|
||||
<ul class="nav navbar-nav navbar-persistent">
|
||||
<li class="active">
|
||||
<a href="#">General</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Roles</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Default Roles</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Credentials</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Token</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Keys</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Email</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">User</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Applications</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">OAuth Clients</a>
|
||||
</li>
|
||||
<!--------------------------------------->
|
||||
|
||||
|
||||
<li class="active pull-right" data-ng-show="auth.loggedIn">
|
||||
<a class="button primary" href="#/create/realm" data-ng-class="path[0] == 'create' && path[1] == 'realm' && 'active'"
|
||||
data-ng-show="auth.loggedIn">Add Realm</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="mock_view" data-ng-view id="view"></div>
|
||||
<div id="loading" class="loading-backdrop">
|
||||
<div class="loading">
|
||||
<span>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.idleTimeout('#idletimeout', '#idletimeout a', {
|
||||
idleAfter: 300,
|
||||
pollingInterval: 60,
|
||||
keepAliveURL: '/auth/rest/admin/keepalive',
|
||||
serverResponseEquals: '',
|
||||
failedRequests: 1,
|
||||
onTimeout: function(){
|
||||
$(this).slideUp();
|
||||
window.location = "/auth/rest/admin/logout";
|
||||
},
|
||||
onIdle: function(){
|
||||
$(this).slideDown(); // show the warning bar
|
||||
},
|
||||
onCountdown: function( counter ){
|
||||
$(this).find("span").html( counter ); // update the counter
|
||||
},
|
||||
onResume: function(){
|
||||
$(this).slideUp(); // hide the warning bar
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,350 +0,0 @@
|
||||
module.controller('OAuthClientClaimsCtrl', function($scope, realm, oauth, claims,
|
||||
OAuthClientClaims,
|
||||
$location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = oauth;
|
||||
$scope.claims = angular.copy(claims);
|
||||
|
||||
$scope.changed = false;
|
||||
|
||||
$scope.$watch('claims', function () {
|
||||
if (!angular.equals($scope.claims, claims)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
|
||||
$scope.save = function () {
|
||||
OAuthClientClaims.update({
|
||||
realm: realm.realm,
|
||||
oauth: oauth.id
|
||||
}, $scope.claims, function () {
|
||||
$scope.changed = false;
|
||||
claims = angular.copy($scope.claims);
|
||||
|
||||
Notifications.success("Your claim changes have been saved.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function () {
|
||||
$location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id + "/claims");
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
module.controller('OAuthClientCredentialsCtrl', function($scope, $location, realm, oauth, OAuthClientCredentials, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = oauth;
|
||||
|
||||
var secret = OAuthClientCredentials.get({ realm : realm.realm, oauth : oauth.id },
|
||||
function() {
|
||||
$scope.secret = secret.value;
|
||||
}
|
||||
);
|
||||
|
||||
$scope.changePassword = function() {
|
||||
var secret = OAuthClientCredentials.update({ realm : realm.realm, oauth : oauth.id },
|
||||
function() {
|
||||
Notifications.success('The secret has been changed.');
|
||||
$scope.secret = secret.value;
|
||||
},
|
||||
function() {
|
||||
Notifications.error("The secret was not changed due to a problem.");
|
||||
$scope.secret = "error";
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, OAuthClient, $location) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauthClients = oauthClients;
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.create = !oauth.id;
|
||||
|
||||
$scope.clientTypes = [
|
||||
"confidential",
|
||||
"public"
|
||||
];
|
||||
|
||||
$scope.changeClientType = function() {
|
||||
console.log('Client Type: ' + $scope.clientType);
|
||||
if ($scope.clientType == "confidential") {
|
||||
$scope.oauth.publicClient = false;
|
||||
} else if ($scope.clientType == "public") {
|
||||
$scope.oauth.publicClient = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!$scope.create) {
|
||||
$scope.oauth= angular.copy(oauth);
|
||||
$scope.clientType = $scope.clientTypes[0];
|
||||
if (oauth.publicClient) {
|
||||
$scope.clientType = $scope.clientTypes[1];
|
||||
}
|
||||
} else {
|
||||
$scope.oauth = { enabled: true };
|
||||
$scope.oauth.webOrigins = [];
|
||||
$scope.oauth.redirectUris = [];
|
||||
$scope.clientType = $scope.clientTypes[0];
|
||||
}
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
|
||||
$scope.$watch('oauth', function() {
|
||||
if (!angular.equals($scope.oauth, oauth)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.deleteWebOrigin = function(index) {
|
||||
$scope.oauth.webOrigins.splice(index, 1);
|
||||
}
|
||||
$scope.addWebOrigin = function() {
|
||||
$scope.oauth.webOrigins.push($scope.newWebOrigin);
|
||||
$scope.newWebOrigin = "";
|
||||
}
|
||||
$scope.deleteRedirectUri = function(index) {
|
||||
$scope.oauth.redirectUris.splice(index, 1);
|
||||
}
|
||||
$scope.addRedirectUri = function() {
|
||||
$scope.oauth.redirectUris.push($scope.newRedirectUri);
|
||||
$scope.newRedirectUri = "";
|
||||
}
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.create) {
|
||||
OAuthClient.save({
|
||||
realm: realm.realm
|
||||
}, $scope.oauth, function (data, headers) {
|
||||
$scope.changed = false;
|
||||
var l = headers().location;
|
||||
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||
$location.url("/realms/" + realm.realm + "/oauth-clients/" + id);
|
||||
Notifications.success("The oauth client has been created.");
|
||||
});
|
||||
} else {
|
||||
OAuthClient.update({
|
||||
realm : realm.realm,
|
||||
id : oauth.id
|
||||
}, $scope.oauth, function() {
|
||||
$scope.changed = false;
|
||||
oauth = angular.copy($scope.oauth);
|
||||
Notifications.success("Your changes have been saved to the oauth client.");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.oauth = angular.copy(oauth);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$location.url("/realms/" + realm.realm + "/oauth-clients");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.oauth.name, 'oauth', function() {
|
||||
$scope.oauth.$remove({
|
||||
realm : realm.realm,
|
||||
id : $scope.oauth.id
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/oauth-clients");
|
||||
Notifications.success("The oauth client has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, roles, applications, OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = oauth;
|
||||
$scope.realmRoles = angular.copy(roles);
|
||||
$scope.selectedRealmRoles = [];
|
||||
$scope.selectedRealmMappings = [];
|
||||
$scope.realmMappings = [];
|
||||
$scope.applications = applications;
|
||||
$scope.applicationRoles = [];
|
||||
$scope.selectedApplicationRoles = [];
|
||||
$scope.selectedApplicationMappings = [];
|
||||
$scope.applicationMappings = [];
|
||||
|
||||
|
||||
|
||||
$scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id}, function(){
|
||||
for (var i = 0; i < $scope.realmMappings.length; i++) {
|
||||
var role = $scope.realmMappings[i];
|
||||
for (var j = 0; j < $scope.realmRoles.length; j++) {
|
||||
var realmRole = $scope.realmRoles[j];
|
||||
if (realmRole.id == role.id) {
|
||||
var idx = $scope.realmRoles.indexOf(realmRole);
|
||||
if (idx != -1) {
|
||||
$scope.realmRoles.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.addRealmRole = function() {
|
||||
$http.post('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
|
||||
$scope.selectedRealmRoles).success(function() {
|
||||
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
|
||||
var role = $scope.selectedRealmRoles[i];
|
||||
var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
|
||||
if (idx != -1) {
|
||||
$scope.realmRoles.splice(idx, 1);
|
||||
$scope.realmMappings.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectRealmRoles = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteRealmRole = function() {
|
||||
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
|
||||
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||
for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
|
||||
var role = $scope.selectedRealmMappings[i];
|
||||
var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
|
||||
if (idx != -1) {
|
||||
$scope.realmMappings.splice(idx, 1);
|
||||
$scope.realmRoles.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedRealmMappings = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addApplicationRole = function() {
|
||||
$http.post('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.name,
|
||||
$scope.selectedApplicationRoles).success(function() {
|
||||
for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
|
||||
var role = $scope.selectedApplicationRoles[i];
|
||||
var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
|
||||
if (idx != -1) {
|
||||
$scope.applicationRoles.splice(idx, 1);
|
||||
$scope.applicationMappings.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedApplicationRoles = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteApplicationRole = function() {
|
||||
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.name,
|
||||
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||
for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
|
||||
var role = $scope.selectedApplicationMappings[i];
|
||||
var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
|
||||
if (idx != -1) {
|
||||
$scope.applicationMappings.splice(idx, 1);
|
||||
$scope.applicationRoles.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedApplicationMappings = [];
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.changeApplication = function() {
|
||||
if ($scope.targetApp) {
|
||||
$scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.targetApp.name}, function() {
|
||||
$scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name}, function(){
|
||||
for (var i = 0; i < $scope.applicationMappings.length; i++) {
|
||||
var role = $scope.applicationMappings[i];
|
||||
for (var j = 0; j < $scope.applicationRoles.length; j++) {
|
||||
var realmRole = $scope.applicationRoles[j];
|
||||
if (realmRole.id == role.id) {
|
||||
var idx = $scope.applicationRoles.indexOf(realmRole);
|
||||
if (idx != -1) {
|
||||
$scope.applicationRoles.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$scope.targetApp = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
module.controller('OAuthClientInstallationCtrl', function($scope, realm, installation, oauth, OAuthClientInstallation, $routeParams) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = oauth;
|
||||
$scope.installation = installation;
|
||||
$scope.download = OAuthClientInstallation.url({ realm: $routeParams.realm, oauth: $routeParams.oauth });
|
||||
});
|
||||
|
||||
module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
|
||||
$scope.oauth = oauth;
|
||||
$scope.realm = realm;
|
||||
var setNotBefore = function() {
|
||||
if ($scope.oauth.notBefore == 0) {
|
||||
$scope.notBefore = "None";
|
||||
} else {
|
||||
$scope.notBefore = new Date($scope.oauth.notBefore * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
setNotBefore();
|
||||
|
||||
var refresh = function() {
|
||||
OAuthClient.get({ realm : realm.realm, id: $scope.oauth.id }, function(updated) {
|
||||
$scope.oauth = updated;
|
||||
setNotBefore();
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
$scope.clear = function() {
|
||||
$scope.oauth.notBefore = 0;
|
||||
OAuthClient.update({ realm : realm.realm, id: $scope.oauth.id}, $scope.oauth, function () {
|
||||
$scope.notBefore = "None";
|
||||
Notifications.success('Not Before cleared for application.');
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
$scope.setNotBeforeNow = function() {
|
||||
$scope.oauth.notBefore = new Date().getTime()/1000;
|
||||
OAuthClient.update({ realm : realm.realm, id: $scope.oauth.id}, $scope.oauth, function () {
|
||||
Notifications.success('Not Before cleared for application.');
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, roles, applications, RealmRoleMapping, ApplicationRoleMapping, ApplicationRole) {
|
||||
$scope.realm = realm;
|
||||
$scope.user = user;
|
||||
$scope.realmRoles = angular.copy(roles);
|
||||
$scope.selectedRealmRoles = [];
|
||||
$scope.selectedRealmMappings = [];
|
||||
$scope.realmMappings = [];
|
||||
$scope.applications = applications;
|
||||
$scope.applicationRoles = [];
|
||||
$scope.selectedApplicationRoles = [];
|
||||
$scope.selectedApplicationMappings = [];
|
||||
$scope.applicationMappings = [];
|
||||
|
||||
$scope.realmMappings = RealmRoleMapping.query({realm : realm.realm, userId : user.username}, function(){
|
||||
for (var i = 0; i < $scope.realmMappings.length; i++) {
|
||||
var role = $scope.realmMappings[i];
|
||||
for (var j = 0; j < $scope.realmRoles.length; j++) {
|
||||
var realmRole = $scope.realmRoles[j];
|
||||
if (realmRole.id == role.id) {
|
||||
var idx = $scope.realmRoles.indexOf(realmRole);
|
||||
if (idx != -1) {
|
||||
$scope.realmRoles.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.addRealmRole = function() {
|
||||
$http.post('/auth/rest/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/realm',
|
||||
$scope.selectedRealmRoles).success(function() {
|
||||
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
|
||||
var role = $scope.selectedRealmRoles[i];
|
||||
var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
|
||||
if (idx != -1) {
|
||||
$scope.realmRoles.splice(idx, 1);
|
||||
$scope.realmMappings.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectRealmRoles = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteRealmRole = function() {
|
||||
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/realm',
|
||||
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||
for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
|
||||
var role = $scope.selectedRealmMappings[i];
|
||||
var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
|
||||
if (idx != -1) {
|
||||
$scope.realmMappings.splice(idx, 1);
|
||||
$scope.realmRoles.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedRealmMappings = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addApplicationRole = function() {
|
||||
$http.post('/auth/rest/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/applications/' + $scope.application.name,
|
||||
$scope.selectedApplicationRoles).success(function() {
|
||||
for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
|
||||
var role = $scope.selectedApplicationRoles[i];
|
||||
var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
|
||||
if (idx != -1) {
|
||||
$scope.applicationRoles.splice(idx, 1);
|
||||
$scope.applicationMappings.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedApplicationRoles = [];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteApplicationRole = function() {
|
||||
$http.delete('/auth/rest/admin/realms/' + realm.realm + '/users/' + user.username + '/role-mappings/applications/' + $scope.application.name,
|
||||
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
|
||||
for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
|
||||
var role = $scope.selectedApplicationMappings[i];
|
||||
var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
|
||||
if (idx != -1) {
|
||||
$scope.applicationMappings.splice(idx, 1);
|
||||
$scope.applicationRoles.push(role);
|
||||
}
|
||||
}
|
||||
$scope.selectedApplicationMappings = [];
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.changeApplication = function() {
|
||||
if ($scope.application) {
|
||||
$scope.applicationRoles = ApplicationRole.query({realm : realm.realm, userId : user.username, application : $scope.application.name}, function() {
|
||||
$scope.applicationMappings = ApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.name}, function(){
|
||||
for (var i = 0; i < $scope.applicationMappings.length; i++) {
|
||||
var role = $scope.applicationMappings[i];
|
||||
for (var j = 0; j < $scope.applicationRoles.length; j++) {
|
||||
var realmRole = $scope.applicationRoles[j];
|
||||
if (realmRole.id == role.id) {
|
||||
var idx = $scope.applicationRoles.indexOf(realmRole);
|
||||
if (idx != -1) {
|
||||
$scope.applicationRoles.splice(idx, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$scope.applicationRoles = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
module.controller('UserSessionsCtrl', function($scope, realm, user, stats, UserLogout, ApplicationLogoutUser, UserSessionStats, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.user = user;
|
||||
$scope.stats = stats;
|
||||
|
||||
$scope.logoutAll = function() {
|
||||
UserLogout.save({realm : realm.realm, user: user.username}, function () {
|
||||
Notifications.success('Logged out user in all applications');
|
||||
UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
|
||||
$scope.stats = updated;
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
$scope.logoutApplication = function(app) {
|
||||
console.log('log user out of app: ' + app);
|
||||
ApplicationLogoutUser.save({realm : realm.realm, application: app, user: user.username}, function () {
|
||||
Notifications.success('Logged out user from application');
|
||||
UserSessionStats.get({realm: realm.realm, user: user.username}, function(updated) {
|
||||
$scope.stats = updated;
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
module.controller('UserListCtrl', function($scope, realm, User) {
|
||||
$scope.realm = realm;
|
||||
$scope.searchQuery = function() {
|
||||
$scope.searchLoaded = false;
|
||||
$scope.currentSearch = $scope.search;
|
||||
|
||||
var params = { realm: realm.realm };
|
||||
if ($scope.search) {
|
||||
params.search = $scope.search;
|
||||
}
|
||||
|
||||
$scope.users = User.query(params, function() {
|
||||
$scope.searchLoaded = true;
|
||||
$scope.lastSearch = params.search;
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, User, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.user = angular.copy(user);
|
||||
$scope.create = !user.username;
|
||||
|
||||
if ($scope.create) {
|
||||
$scope.user.enabled = true;
|
||||
}
|
||||
|
||||
$scope.changed = false; // $scope.create;
|
||||
|
||||
// ID - Name map for required actions. IDs are enum names.
|
||||
$scope.userReqActionList = [
|
||||
{id: "VERIFY_EMAIL", text: "Verify Email"},
|
||||
{id: "UPDATE_PROFILE", text: "Update Profile"},
|
||||
{id: "CONFIGURE_TOTP", text: "Configure Totp"},
|
||||
{id: "UPDATE_PASSWORD", text: "Update Password"}
|
||||
];
|
||||
|
||||
$scope.$watch('user', function() {
|
||||
if (!angular.equals($scope.user, user)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.create) {
|
||||
User.save({
|
||||
realm: realm.realm
|
||||
}, $scope.user, function () {
|
||||
$scope.changed = false;
|
||||
user = angular.copy($scope.user);
|
||||
|
||||
$location.url("/realms/" + realm.realm + "/users/" + $scope.user.username);
|
||||
Notifications.success("The user has been created.");
|
||||
});
|
||||
} else {
|
||||
User.update({
|
||||
realm: realm.realm,
|
||||
userId: $scope.user.username
|
||||
}, $scope.user, function () {
|
||||
$scope.changed = false;
|
||||
user = angular.copy($scope.user);
|
||||
Notifications.success("Your changes have been saved to the user.");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy(user);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$location.url("/realms/" + realm.realm + "/users");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.user.username, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : realm.realm,
|
||||
userId : $scope.user.username
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.realm + "/users");
|
||||
Notifications.success("The user has been deleted.");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications, Dialog) {
|
||||
console.log('UserCredentialsCtrl');
|
||||
|
||||
$scope.realm = realm;
|
||||
$scope.user = angular.copy(user);
|
||||
|
||||
$scope.isTotp = false;
|
||||
if(!!user.totp){
|
||||
$scope.isTotp = user.totp;
|
||||
}
|
||||
|
||||
$scope.resetPassword = function() {
|
||||
if ($scope.pwdChange) {
|
||||
if ($scope.password != $scope.confirmPassword) {
|
||||
Notifications.error("Password and confirmation does not match.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Dialog.confirm('Reset password', 'Are you sure you want to reset the users password?', function() {
|
||||
UserCredentials.resetPassword({ realm: realm.realm, userId: user.username }, { type : "password", value : $scope.password }, function() {
|
||||
Notifications.success("The password has been reset");
|
||||
$scope.password = null;
|
||||
$scope.confirmPassword = null;
|
||||
}, function() {
|
||||
Notifications.error("Failed to reset user password");
|
||||
});
|
||||
}, function() {
|
||||
$scope.password = null;
|
||||
$scope.confirmPassword = null;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeTotp = function() {
|
||||
Dialog.confirm('Remove totp', 'Are you sure you want to remove the users totp configuration?', function() {
|
||||
UserCredentials.removeTotp({ realm: realm.realm, userId: user.username }, { }, function() {
|
||||
Notifications.success("The users totp configuration has been removed");
|
||||
$scope.user.totp = false;
|
||||
}, function() {
|
||||
Notifications.error("Failed to remove the users totp configuration");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.resetPasswordEmail = function() {
|
||||
Dialog.confirm('Reset password email', 'Are you sure you want to send password reset email to user?', function() {
|
||||
UserCredentials.resetPasswordEmail({ realm: realm.realm, userId: user.username }, { }, function() {
|
||||
Notifications.success("Password reset email sent to user");
|
||||
}, function() {
|
||||
Notifications.error("Failed to send password reset mail to user");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch('user', function() {
|
||||
if (!angular.equals($scope.user, user)) {
|
||||
$scope.userChange = true;
|
||||
} else {
|
||||
$scope.userChange = false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('password', function() {
|
||||
if (!!$scope.password){
|
||||
$scope.pwdChange = true;
|
||||
} else {
|
||||
$scope.pwdChange = false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.password = "";
|
||||
$scope.confirmPassword = "";
|
||||
|
||||
$scope.user = angular.copy(user);
|
||||
|
||||
$scope.isTotp = false;
|
||||
if(!!user.totp){
|
||||
$scope.isTotp = user.totp;
|
||||
}
|
||||
|
||||
$scope.pwdChange = false;
|
||||
$scope.userChange = false;
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RoleMappingCtrl', function($scope, realm, User, users, role, RoleMapping, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.realmId = realm.realm || realm.realm;
|
||||
$scope.allUsers = User.query({ realm : $scope.realmId });
|
||||
$scope.users = users;
|
||||
$scope.role = role;
|
||||
|
||||
$scope.addUser = function() {
|
||||
var user = $scope.newUser;
|
||||
$scope.newUser = null;
|
||||
|
||||
for ( var i = 0; i < $scope.allUsers.length; i++) {
|
||||
if ($scope.allUsers[i].userId == user) {
|
||||
user = $scope.allUsers[i];
|
||||
RoleMapping.save({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
Notifications.success("The role mapping has been added for the user.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeUser = function(userId) {
|
||||
for (var i = 0; i < $scope.users.length; i++) {
|
||||
var user = $scope.users[i];
|
||||
if ($scope.users[i].userId == userId) {
|
||||
RoleMapping.delete({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
|
||||
Notifications.success("The role mapping has been removed for the user.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<div class="modal-dialog" tabindex="-1" role="dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{{ title }}</h4>
|
||||
</div>
|
||||
<div class="modal-body"><p ng-bind-html-unsafe="message"></p></div>
|
||||
<div class="modal-footer">
|
||||
<button ng-repeat="btn in buttons" ng-click="close(btn.result)" class="" ng-class="btn.cssClass">{{ btn.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd ">
|
||||
</web-fragment>
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<name>Keycloak Audit API</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,149 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Audit {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Audit.class);
|
||||
|
||||
private List<AuditListener> listeners;
|
||||
private Event event;
|
||||
|
||||
public Audit(List<AuditListener> listeners, RealmModel realm, String ipAddress) {
|
||||
this.listeners = listeners;
|
||||
this.event = new Event();
|
||||
|
||||
realm(realm);
|
||||
ipAddress(ipAddress);
|
||||
}
|
||||
|
||||
Audit() {
|
||||
}
|
||||
|
||||
public Audit realm(RealmModel realm) {
|
||||
event.setRealmId(realm.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit realm(String realmId) {
|
||||
event.setRealmId(realmId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit client(ClientModel client) {
|
||||
event.setClientId(client.getClientId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit client(String clientId) {
|
||||
event.setClientId(clientId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit user(UserModel user) {
|
||||
event.setUserId(user.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit user(String userId) {
|
||||
event.setUserId(userId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit session(UserSessionModel session) {
|
||||
event.setSessionId(session.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit session(String sessionId) {
|
||||
event.setSessionId(sessionId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit ipAddress(String ipAddress) {
|
||||
event.setIpAddress(ipAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit event(EventType e) {
|
||||
event.setEvent(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit detail(String key, String value) {
|
||||
if (value == null || value.equals("")) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (event.getDetails() == null) {
|
||||
event.setDetails(new HashMap<String, String>());
|
||||
}
|
||||
event.getDetails().put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Audit removeDetail(String key) {
|
||||
if (event.getDetails() != null) {
|
||||
event.getDetails().remove(key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Event getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void success() {
|
||||
send();
|
||||
}
|
||||
|
||||
public void error(String error) {
|
||||
event.setEvent(EventType.valueOf(event.getEvent().name() + "_ERROR"));
|
||||
event.setError(error);
|
||||
send();
|
||||
}
|
||||
|
||||
public Audit clone() {
|
||||
Audit clone = new Audit();
|
||||
clone.listeners = listeners;
|
||||
clone.event = event.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
public Audit reset() {
|
||||
Event old = event;
|
||||
|
||||
event = new Event();
|
||||
event.setRealmId(old.getRealmId());
|
||||
event.setIpAddress(old.getIpAddress());
|
||||
event.setClientId(old.getClientId());
|
||||
event.setUserId(old.getUserId());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void send() {
|
||||
event.setTime(System.currentTimeMillis());
|
||||
|
||||
if (listeners != null) {
|
||||
for (AuditListener l : listeners) {
|
||||
try {
|
||||
l.onEvent(event);
|
||||
} catch (Throwable t) {
|
||||
log.error("Failed to send event to " + l, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface AuditListener extends Provider {
|
||||
|
||||
public void onEvent(Event event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface AuditListenerFactory extends ProviderFactory<AuditListener> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AuditListenerSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "audit-listener";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return AuditListener.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return AuditListenerFactory.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface AuditProvider extends AuditListener {
|
||||
|
||||
public EventQuery createQuery();
|
||||
|
||||
public void clear();
|
||||
|
||||
public void clear(String realmId);
|
||||
|
||||
public void clear(String realmId, long olderThan);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface AuditProviderFactory extends ProviderFactory<AuditProvider> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class AuditProviderSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "audit";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return AuditProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return AuditProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface Details {
|
||||
|
||||
String EMAIL = "email";
|
||||
String PREVIOUS_EMAIL = "previous_email";
|
||||
String UPDATED_EMAIL = "updated_email";
|
||||
String CODE_ID = "code_id";
|
||||
String REDIRECT_URI = "redirect_uri";
|
||||
String RESPONSE_TYPE = "response_type";
|
||||
String AUTH_METHOD = "auth_method";
|
||||
String REGISTER_METHOD = "register_method";
|
||||
String USERNAME = "username";
|
||||
String REMEMBER_ME = "remember_me";
|
||||
String TOKEN_ID = "token_id";
|
||||
String REFRESH_TOKEN_ID = "refresh_token_id";
|
||||
String VALIDATE_ACCESS_TOKEN = "validate_access_token";
|
||||
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface Errors {
|
||||
|
||||
String REALM_DISABLED = "realm_disabled";
|
||||
|
||||
String CLIENT_NOT_FOUND = "client_not_found";
|
||||
String CLIENT_DISABLED = "client_disabled";
|
||||
String INVALID_CLIENT_CREDENTIALS = "invalid_client_credentials";
|
||||
String INVALID_CLIENT = "invalid_client";
|
||||
|
||||
String USER_NOT_FOUND = "user_not_found";
|
||||
String USER_DISABLED = "user_disabled";
|
||||
String USER_TEMPORARILY_DISABLED = "user_temporarily_disabled";
|
||||
String INVALID_USER_CREDENTIALS = "invalid_user_credentials";
|
||||
|
||||
String USERNAME_MISSING = "username_missing";
|
||||
String USERNAME_IN_USE = "username_in_use";
|
||||
|
||||
String INVALID_REDIRECT_URI = "invalid_redirect_uri";
|
||||
String INVALID_CODE = "invalid_code";
|
||||
String INVALID_TOKEN = "invalid_token";
|
||||
String INVALID_REGISTRATION = "invalid_registration";
|
||||
String INVALID_FORM = "invalid_form";
|
||||
|
||||
String REGISTRATION_DISABLED = "registration_disabled";
|
||||
|
||||
String REJECTED_BY_USER = "rejected_by_user";
|
||||
|
||||
String NOT_ALLOWED = "not_allowed";
|
||||
|
||||
String SOCIAL_PROVIDER_NOT_FOUND = "social_provider_not_found";
|
||||
String SOCIAL_ID_IN_USE = "social_id_in_use";
|
||||
|
||||
String USER_NOT_LOGGED_IN = "user_not_logged_in";
|
||||
String USER_SESSION_NOT_FOUND = "user_session_not_found";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Event {
|
||||
|
||||
private long time;
|
||||
|
||||
private EventType event;
|
||||
|
||||
private String realmId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String userId;
|
||||
|
||||
private String sessionId;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private String error;
|
||||
|
||||
private Map<String, String> details;
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public EventType getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(EventType event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public Event clone() {
|
||||
Event clone = new Event();
|
||||
clone.time = time;
|
||||
clone.event = event;
|
||||
clone.realmId = realmId;
|
||||
clone.clientId = clientId;
|
||||
clone.userId = userId;
|
||||
clone.sessionId = sessionId;
|
||||
clone.ipAddress = ipAddress;
|
||||
clone.error = error;
|
||||
clone.details = details != null ? new HashMap<String, String>(details) : null;
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface EventQuery {
|
||||
|
||||
public EventQuery event(EventType... events);
|
||||
|
||||
public EventQuery realm(String realmId);
|
||||
|
||||
public EventQuery client(String clientId);
|
||||
|
||||
public EventQuery user(String userId);
|
||||
|
||||
public EventQuery ipAddress(String ipAddress);
|
||||
|
||||
public EventQuery firstResult(int result);
|
||||
|
||||
public EventQuery maxResults(int results);
|
||||
|
||||
public List<Event> getResultList();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.keycloak.audit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public enum EventType {
|
||||
|
||||
LOGIN,
|
||||
LOGIN_ERROR,
|
||||
REGISTER,
|
||||
REGISTER_ERROR,
|
||||
LOGOUT,
|
||||
LOGOUT_ERROR,
|
||||
CODE_TO_TOKEN,
|
||||
CODE_TO_TOKEN_ERROR,
|
||||
REFRESH_TOKEN,
|
||||
VALIDATE_ACCESS_TOKEN,
|
||||
VALIDATE_ACCESS_TOKEN_ERROR,
|
||||
REFRESH_TOKEN_ERROR,
|
||||
SOCIAL_LINK,
|
||||
SOCIAL_LINK_ERROR,
|
||||
REMOVE_SOCIAL_LINK,
|
||||
REMOVE_SOCIAL_LINK_ERROR,
|
||||
|
||||
UPDATE_EMAIL,
|
||||
UPDATE_EMAIL_ERROR,
|
||||
UPDATE_PROFILE,
|
||||
UPDATE_PROFILE_ERROR,
|
||||
UPDATE_PASSWORD,
|
||||
UPDATE_PASSWORD_ERROR,
|
||||
UPDATE_TOTP,
|
||||
UPDATE_TOTP_ERROR,
|
||||
VERIFY_EMAIL,
|
||||
VERIFY_EMAIL_ERROR,
|
||||
|
||||
REMOVE_TOTP,
|
||||
REMOVE_TOTP_ERROR,
|
||||
|
||||
SEND_VERIFY_EMAIL,
|
||||
SEND_VERIFY_EMAIL_ERROR,
|
||||
SEND_RESET_PASSWORD,
|
||||
SEND_RESET_PASSWORD_ERROR
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.keycloak.audit.AuditProviderSpi
|
||||
org.keycloak.audit.AuditListenerSpi
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-audit-email</artifactId>
|
||||
<name>Keycloak Audit Email Provider</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-email-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.keycloak.audit.email;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.audit.AuditListener;
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class EmailAuditListener implements AuditListener {
|
||||
|
||||
private static final Logger log = Logger.getLogger(EmailAuditListener.class);
|
||||
|
||||
private KeycloakSession session;
|
||||
private RealmProvider model;
|
||||
private EmailProvider emailProvider;
|
||||
private Set<EventType> includedEvents;
|
||||
|
||||
public EmailAuditListener(KeycloakSession session, EmailProvider emailProvider, Set<EventType> includedEvents) {
|
||||
this.session = session;
|
||||
this.model = session.realms();
|
||||
this.emailProvider = emailProvider;
|
||||
this.includedEvents = includedEvents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (includedEvents.contains(event.getEvent())) {
|
||||
if (event.getRealmId() != null && event.getUserId() != null) {
|
||||
RealmModel realm = model.getRealm(event.getRealmId());
|
||||
UserModel user = session.users().getUserById(event.getUserId(), realm);
|
||||
if (user != null && user.getEmail() != null && user.isEmailVerified()) {
|
||||
try {
|
||||
emailProvider.setRealm(realm).setUser(user).sendEvent(event);
|
||||
} catch (EmailException e) {
|
||||
log.error("Failed to send event mail", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.keycloak.audit.email;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.audit.AuditListener;
|
||||
import org.keycloak.audit.AuditListenerFactory;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class EmailAuditListenerFactory implements AuditListenerFactory {
|
||||
|
||||
private static final Set<EventType> SUPPORTED_EVENTS = new HashSet<EventType>();
|
||||
static {
|
||||
Collections.addAll(SUPPORTED_EVENTS, EventType.LOGIN_ERROR, EventType.UPDATE_PASSWORD, EventType.REMOVE_TOTP, EventType.UPDATE_TOTP);
|
||||
}
|
||||
|
||||
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||
|
||||
@Override
|
||||
public AuditListener create(KeycloakSession session) {
|
||||
EmailProvider emailProvider = session.getProvider(EmailProvider.class);
|
||||
return new EmailAuditListener(session, emailProvider, includedEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String[] include = config.getArray("include-events");
|
||||
if (include != null) {
|
||||
for (String i : include) {
|
||||
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||
}
|
||||
} else {
|
||||
includedEvents.addAll(SUPPORTED_EVENTS);
|
||||
}
|
||||
|
||||
String[] exclude = config.getArray("exclude-events");
|
||||
if (exclude != null) {
|
||||
for (String e : exclude) {
|
||||
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "email";
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.audit.email.EmailAuditListenerFactory
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-audit-jboss-logging</artifactId>
|
||||
<name>Keycloak Audit JBoss Logging Provider</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package org.keycloak.audit.log;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.audit.AuditListener;
|
||||
import org.keycloak.audit.Event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JBossLoggingAuditListener implements AuditListener {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public JBossLoggingAuditListener(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
Logger.Level level = event.getError() != null ? Logger.Level.WARN : Logger.Level.INFO;
|
||||
|
||||
if (logger.isEnabled(level)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("event=");
|
||||
sb.append(event.getEvent());
|
||||
sb.append(", realmId=");
|
||||
sb.append(event.getRealmId());
|
||||
sb.append(", clientId=");
|
||||
sb.append(event.getClientId());
|
||||
sb.append(", userId=");
|
||||
sb.append(event.getUserId());
|
||||
sb.append(", ipAddress=");
|
||||
sb.append(event.getIpAddress());
|
||||
|
||||
if (event.getError() != null) {
|
||||
sb.append(", error=");
|
||||
sb.append(event.getError());
|
||||
}
|
||||
|
||||
if (event.getDetails() != null) {
|
||||
for (Map.Entry<String, String> e : event.getDetails().entrySet()) {
|
||||
sb.append(", ");
|
||||
sb.append(e.getKey());
|
||||
if (e.getValue() == null || e.getValue().indexOf(' ') == -1) {
|
||||
sb.append("=");
|
||||
sb.append(e.getValue());
|
||||
} else {
|
||||
sb.append("='");
|
||||
sb.append(e.getValue());
|
||||
sb.append("'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(level, sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package org.keycloak.audit.log;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.audit.AuditListener;
|
||||
import org.keycloak.audit.AuditListenerFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
|
||||
|
||||
public static final String ID = "jboss-logging";
|
||||
|
||||
private static final Logger logger = Logger.getLogger("org.keycloak.audit");
|
||||
|
||||
@Override
|
||||
public AuditListener create(KeycloakSession session) {
|
||||
return new JBossLoggingAuditListener(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.audit.log.JBossLoggingAuditListenerFactory
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-audit-jpa</artifactId>
|
||||
<name>Keycloak Audit JPA Provider</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>${hibernate.entitymanager.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.keycloak.audit.jpa;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name="EVENT_ENTITY")
|
||||
public class EventEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="ID", length = 36)
|
||||
private String id;
|
||||
|
||||
@Column(name="TIME")
|
||||
private long time;
|
||||
|
||||
@Column(name="EVENT")
|
||||
private String event;
|
||||
|
||||
@Column(name="REALM_ID")
|
||||
private String realmId;
|
||||
|
||||
@Column(name="CLIENT_ID")
|
||||
private String clientId;
|
||||
|
||||
@Column(name="USER_ID")
|
||||
private String userId;
|
||||
|
||||
@Column(name="SESSION_ID")
|
||||
private String sessionId;
|
||||
|
||||
@Column(name="IP_ADDRESS")
|
||||
private String ipAddress;
|
||||
|
||||
@Column(name="ERROR")
|
||||
private String error;
|
||||
|
||||
@Column(name="DETAILS_JSON", length = 2550)
|
||||
private String detailsJson;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public String getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setEvent(String event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getDetailsJson() {
|
||||
return detailsJson;
|
||||
}
|
||||
|
||||
public void setDetailsJson(String detailsJson) {
|
||||
this.detailsJson = detailsJson;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.keycloak.audit.jpa;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.type.TypeReference;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.audit.EventQuery;
|
||||
import org.keycloak.audit.EventType;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityTransaction;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaAuditProvider implements AuditProvider {
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
private static final TypeReference<Map<String, String>> mapType = new TypeReference<Map<String, String>>() {
|
||||
};
|
||||
private static final Logger logger = Logger.getLogger(JpaAuditProvider.class);
|
||||
|
||||
private EntityManager em;
|
||||
private EntityTransaction tx;
|
||||
private Set<EventType> includedEvents;
|
||||
|
||||
public JpaAuditProvider(EntityManager em, Set<EventType> includedEvents) {
|
||||
this.em = em;
|
||||
this.includedEvents = includedEvents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery createQuery() {
|
||||
return new JpaEventQuery(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
em.createQuery("delete from EventEntity").executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId) {
|
||||
em.createQuery("delete from EventEntity where realmId = :realmId").setParameter("realmId", realmId).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId, long olderThan) {
|
||||
em.createQuery("delete from EventEntity where realmId = :realmId and time < :time").setParameter("realmId", realmId).setParameter("time", olderThan).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (includedEvents.contains(event.getEvent())) {
|
||||
em.persist(convert(event));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
static EventEntity convert(Event o) {
|
||||
EventEntity e = new EventEntity();
|
||||
e.setId(UUID.randomUUID().toString());
|
||||
e.setTime(o.getTime());
|
||||
e.setEvent(o.getEvent().toString());
|
||||
e.setRealmId(o.getRealmId());
|
||||
e.setClientId(o.getClientId());
|
||||
e.setUserId(o.getUserId());
|
||||
e.setSessionId(o.getSessionId());
|
||||
e.setIpAddress(o.getIpAddress());
|
||||
e.setError(o.getError());
|
||||
try {
|
||||
e.setDetailsJson(mapper.writeValueAsString(o.getDetails()));
|
||||
} catch (IOException ex) {
|
||||
logger.error("Failed to write log details", ex);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
static Event convert(EventEntity o) {
|
||||
Event e = new Event();
|
||||
e.setTime(o.getTime());
|
||||
e.setEvent(EventType.valueOf(o.getEvent()));
|
||||
e.setRealmId(o.getRealmId());
|
||||
e.setClientId(o.getClientId());
|
||||
e.setUserId(o.getUserId());
|
||||
e.setSessionId(o.getSessionId());
|
||||
e.setIpAddress(o.getIpAddress());
|
||||
e.setError(o.getError());
|
||||
try {
|
||||
Map<String, String> details = mapper.readValue(o.getDetailsJson(), mapType);
|
||||
e.setDetails(details);
|
||||
} catch (IOException ex) {
|
||||
logger.error("Failed to read log details", ex);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.keycloak.audit.jpa;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaAuditProviderFactory implements AuditProviderFactory {
|
||||
|
||||
public static final String ID = "jpa";
|
||||
|
||||
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||
|
||||
@Override
|
||||
public AuditProvider create(KeycloakSession session) {
|
||||
JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class);
|
||||
return new JpaAuditProvider(connection.getEntityManager(), includedEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String[] include = config.getArray("include-events");
|
||||
if (include != null) {
|
||||
for (String i : include) {
|
||||
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||
}
|
||||
} else {
|
||||
for (EventType i : EventType.values()) {
|
||||
includedEvents.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
String[] exclude = config.getArray("exclude-events");
|
||||
if (exclude != null) {
|
||||
for (String e : exclude) {
|
||||
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.keycloak.audit.jpa;
|
||||
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.audit.EventQuery;
|
||||
import org.keycloak.audit.EventType;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaEventQuery implements EventQuery {
|
||||
|
||||
private final EntityManager em;
|
||||
private final CriteriaBuilder cb;
|
||||
private final CriteriaQuery<EventEntity> cq;
|
||||
private final Root<EventEntity> root;
|
||||
private final ArrayList<Predicate> predicates;
|
||||
private Integer firstResult;
|
||||
private Integer maxResults;
|
||||
|
||||
public JpaEventQuery(EntityManager em) {
|
||||
this.em = em;
|
||||
|
||||
cb = em.getCriteriaBuilder();
|
||||
cq = cb.createQuery(EventEntity.class);
|
||||
root = cq.from(EventEntity.class);
|
||||
predicates = new ArrayList<Predicate>(4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery event(EventType... events) {
|
||||
List<String> eventStrings = new LinkedList<String>();
|
||||
for (EventType e : events) {
|
||||
eventStrings.add(e.toString());
|
||||
}
|
||||
predicates.add(root.get("event").in(eventStrings));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery realm(String realmId) {
|
||||
predicates.add(cb.equal(root.get("realmId"), realmId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery client(String clientId) {
|
||||
predicates.add(cb.equal(root.get("clientId"), clientId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery user(String userId) {
|
||||
predicates.add(cb.equal(root.get("userId"), userId));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery ipAddress(String ipAddress) {
|
||||
predicates.add(cb.equal(root.get("ipAddress"), ipAddress));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery firstResult(int firstResult) {
|
||||
this.firstResult = firstResult;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery maxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Event> getResultList() {
|
||||
if (!predicates.isEmpty()) {
|
||||
cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
|
||||
}
|
||||
|
||||
cq.orderBy(cb.desc(root.get("time")));
|
||||
|
||||
TypedQuery<EventEntity> query = em.createQuery(cq);
|
||||
|
||||
if (firstResult != null) {
|
||||
query.setFirstResult(firstResult);
|
||||
}
|
||||
|
||||
if (maxResults != null) {
|
||||
query.setMaxResults(maxResults);
|
||||
}
|
||||
|
||||
List<Event> events = new LinkedList<Event>();
|
||||
for (EventEntity e : query.getResultList()) {
|
||||
events.add(JpaAuditProvider.convert(e));
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.keycloak.audit.jpa.JpaAuditProviderFactory
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-audit-mongo</artifactId>
|
||||
<name>Keycloak Audit Mongo Provider</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.keycloak.audit.mongo;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.audit.EventQuery;
|
||||
import org.keycloak.audit.EventType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoAuditProvider implements AuditProvider {
|
||||
|
||||
private DBCollection audit;
|
||||
private Set<EventType> includedEvents;
|
||||
|
||||
public MongoAuditProvider(DBCollection audit, Set<EventType> includedEvents) {
|
||||
this.audit = audit;
|
||||
this.includedEvents = includedEvents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery createQuery() {
|
||||
return new MongoEventQuery(audit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
audit.remove(new BasicDBObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId) {
|
||||
audit.remove(new BasicDBObject("realmId", realmId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId, long olderThan) {
|
||||
BasicDBObject q = new BasicDBObject();
|
||||
q.put("realmId", realmId);
|
||||
q.put("time", new BasicDBObject("$lt", olderThan));
|
||||
audit.remove(q);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (includedEvents.contains(event.getEvent())) {
|
||||
audit.insert(convert(event));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
static DBObject convert(Event o) {
|
||||
BasicDBObject e = new BasicDBObject();
|
||||
e.put("time", o.getTime());
|
||||
e.put("event", o.getEvent().toString());
|
||||
e.put("realmId", o.getRealmId());
|
||||
e.put("clientId", o.getClientId());
|
||||
e.put("userId", o.getUserId());
|
||||
e.put("sessionId", o.getSessionId());
|
||||
e.put("ipAddress", o.getIpAddress());
|
||||
e.put("error", o.getError());
|
||||
|
||||
BasicDBObject details = new BasicDBObject();
|
||||
if (o.getDetails() != null) {
|
||||
for (Map.Entry<String, String> entry : o.getDetails().entrySet()) {
|
||||
details.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
e.put("details", details);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static Event convert(BasicDBObject o) {
|
||||
Event e = new Event();
|
||||
e.setTime(o.getLong("time"));
|
||||
e.setEvent(EventType.valueOf(o.getString("event")));
|
||||
e.setRealmId(o.getString("realmId"));
|
||||
e.setClientId(o.getString("clientId"));
|
||||
e.setUserId(o.getString("userId"));
|
||||
e.setSessionId(o.getString("sessionId"));
|
||||
e.setIpAddress(o.getString("ipAddress"));
|
||||
e.setError(o.getString("error"));
|
||||
|
||||
BasicDBObject d = (BasicDBObject) o.get("details");
|
||||
if (d != null) {
|
||||
Map<String, String> details = new HashMap<String, String>();
|
||||
for (Object k : d.keySet()) {
|
||||
details.put((String) k, d.getString((String) k));
|
||||
}
|
||||
e.setDetails(details);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.keycloak.audit.mongo;
|
||||
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.WriteConcern;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.connections.mongo.MongoConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoAuditProviderFactory implements AuditProviderFactory {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(MongoAuditProviderFactory.class);
|
||||
|
||||
public static final String ID = "mongo";
|
||||
|
||||
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||
|
||||
@Override
|
||||
public AuditProvider create(KeycloakSession session) {
|
||||
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
|
||||
|
||||
DBCollection collection = connection.getDB().getCollection("audit");
|
||||
collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
|
||||
|
||||
return new MongoAuditProvider(collection, includedEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String[] include = config.getArray("include-events");
|
||||
if (include != null) {
|
||||
for (String i : include) {
|
||||
includedEvents.add(EventType.valueOf(i.toUpperCase()));
|
||||
}
|
||||
} else {
|
||||
for (EventType i : EventType.values()) {
|
||||
includedEvents.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
String[] exclude = config.getArray("exclude-events");
|
||||
if (exclude != null) {
|
||||
for (String e : exclude) {
|
||||
includedEvents.remove(EventType.valueOf(e.toUpperCase()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.keycloak.audit.mongo;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.audit.EventQuery;
|
||||
import org.keycloak.audit.EventType;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoEventQuery implements EventQuery {
|
||||
|
||||
private Integer firstResult;
|
||||
private Integer maxResults;
|
||||
private DBCollection audit;
|
||||
private final BasicDBObject query;
|
||||
|
||||
public MongoEventQuery(DBCollection audit) {
|
||||
this.audit = audit;
|
||||
query = new BasicDBObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery event(EventType... events) {
|
||||
List<String> eventStrings = new LinkedList<String>();
|
||||
for (EventType e : events) {
|
||||
eventStrings.add(e.toString());
|
||||
}
|
||||
query.put("event", new BasicDBObject("$in", eventStrings));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery realm(String realmId) {
|
||||
query.put("realmId", realmId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery client(String clientId) {
|
||||
query.put("clientId", clientId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery user(String userId) {
|
||||
query.put("userId", userId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery ipAddress(String ipAddress) {
|
||||
query.put("ipAddress", ipAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery firstResult(int firstResult) {
|
||||
this.firstResult = firstResult;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQuery maxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Event> getResultList() {
|
||||
DBCursor cur = audit.find(query).sort(new BasicDBObject("time", -1));
|
||||
if (firstResult != null) {
|
||||
cur.skip(firstResult);
|
||||
}
|
||||
if (maxResults != null) {
|
||||
cur.limit(maxResults);
|
||||
}
|
||||
|
||||
List<Event> events = new LinkedList<Event>();
|
||||
while (cur.hasNext()) {
|
||||
events.add(MongoAuditProvider.convert((BasicDBObject) cur.next()));
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.audit.mongo.MongoAuditProviderFactory
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Audit Parent</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>api</module>
|
||||
<module>email</module>
|
||||
<module>jpa</module>
|
||||
<module>jboss-logging</module>
|
||||
<module>mongo</module>
|
||||
</modules>
|
||||
</project>
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<name>Keycloak Connections JPA</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>${hibernate.entitymanager.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultJpaConnectionProvider implements JpaConnectionProvider {
|
||||
|
||||
private final EntityManager em;
|
||||
|
||||
public DefaultJpaConnectionProvider(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getEntityManager() {
|
||||
return em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
em.close();
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.hibernate.ejb.AvailableSettings;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
|
||||
|
||||
private volatile EntityManagerFactory emf;
|
||||
|
||||
private Config.Scope config;
|
||||
|
||||
@Override
|
||||
public JpaConnectionProvider create(KeycloakSession session) {
|
||||
lazyInit();
|
||||
|
||||
EntityManager em = emf.createEntityManager();
|
||||
em = PersistenceExceptionConverter.create(em);
|
||||
session.getTransaction().enlist(new JpaKeycloakTransaction(em));
|
||||
return new DefaultJpaConnectionProvider(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (emf != null) {
|
||||
emf.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private void lazyInit() {
|
||||
if (emf == null) {
|
||||
synchronized (this) {
|
||||
if (emf == null) {
|
||||
String unitName = config.get("unitName");
|
||||
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 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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
String password = config.get("password");
|
||||
if (password != null) {
|
||||
properties.put(AvailableSettings.JDBC_PASSWORD, password);
|
||||
}
|
||||
}
|
||||
|
||||
String databaseSchema = config.get("databaseSchema", "validate");
|
||||
if (databaseSchema != null) {
|
||||
properties.put("hibernate.hbm2ddl.auto", databaseSchema);
|
||||
}
|
||||
|
||||
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
|
||||
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
|
||||
}
|
||||
|
||||
emf = Persistence.createEntityManagerFactory(unitName, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface JpaConnectionProvider extends Provider {
|
||||
|
||||
EntityManager getEntityManager();
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsJpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return JpaConnectionProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return JpaConnectionProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JpaKeycloakTransaction implements KeycloakTransaction {
|
||||
|
||||
protected EntityManager em;
|
||||
|
||||
public JpaKeycloakTransaction(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
em.getTransaction().begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
em.getTransaction().commit();
|
||||
} catch (PersistenceException e) {
|
||||
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
em.getTransaction().setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return em.getTransaction().getRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return em.getTransaction().isActive();
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
|
||||
import javax.persistence.EntityExistsException;
|
||||
import javax.persistence.EntityManager;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class PersistenceExceptionConverter implements InvocationHandler {
|
||||
|
||||
private EntityManager em;
|
||||
|
||||
public static EntityManager create(EntityManager em) {
|
||||
return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
|
||||
}
|
||||
|
||||
private PersistenceExceptionConverter(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
try {
|
||||
return method.invoke(em, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw convert(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public static ModelException convert(Throwable t) {
|
||||
if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
|
||||
throw new ModelDuplicateException(t);
|
||||
} if (t instanceof EntityExistsException) {
|
||||
throw new ModelDuplicateException(t);
|
||||
} else {
|
||||
throw new ModelException(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+16
-6
@@ -2,24 +2,34 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
|
||||
version="1.0">
|
||||
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
|
||||
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
|
||||
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
|
||||
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ApplicationRoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RealmRoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
|
||||
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<!-- 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.UserSessionEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
|
||||
|
||||
<!-- JpaAuditProvider -->
|
||||
<class>org.keycloak.audit.jpa.EventEntity</class>
|
||||
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
|
||||
<properties>
|
||||
<property name="hibernate.hbm2ddl.auto" value="update" />
|
||||
<property name="jboss.as.jpa.managed" value="false"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory
|
||||
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.jpa.JpaConnectionSpi
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<name>Keycloak Connections Mongo</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocationContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
|
||||
|
||||
// TODO Make configurable
|
||||
private String[] entities = new String[]{
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoUserEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
|
||||
"org.keycloak.models.entities.RequiredCredentialEntity",
|
||||
"org.keycloak.models.entities.CredentialEntity",
|
||||
"org.keycloak.models.entities.SocialLinkEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
|
||||
"org.keycloak.models.entities.UserFederationProviderEntity"
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
|
||||
|
||||
private volatile MongoClient client;
|
||||
|
||||
private MongoStore mongoStore;
|
||||
private DB db;
|
||||
private Config.Scope config;
|
||||
|
||||
@Override
|
||||
public MongoConnectionProvider create(KeycloakSession session) {
|
||||
lazyInit();
|
||||
|
||||
TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
|
||||
session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
|
||||
return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private void lazyInit() {
|
||||
if (client == null) {
|
||||
synchronized (this) {
|
||||
if (client == null) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak");
|
||||
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
|
||||
} else {
|
||||
client = new MongoClient(host, port);
|
||||
}
|
||||
|
||||
this.db = client.getDB(dbName);
|
||||
|
||||
this.mongoStore = new MongoStoreImpl(db, clearOnStartup, getManagedEntities());
|
||||
|
||||
logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Class[] getManagedEntities() throws ClassNotFoundException {
|
||||
Class[] entityClasses = new Class[entities.length];
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
entityClasses[i] = Thread.currentThread().getContextClassLoader().loadClass(entities[i]);
|
||||
}
|
||||
return entityClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultMongoConnectionProvider implements MongoConnectionProvider {
|
||||
|
||||
private DB db;
|
||||
private MongoStore mongoStore;
|
||||
private MongoStoreInvocationContext invocationContext;
|
||||
|
||||
public DefaultMongoConnectionProvider(DB db, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
|
||||
this.db = db;
|
||||
this.mongoStore = mongoStore;
|
||||
this.invocationContext = invocationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DB getDB() {
|
||||
return db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoStore getMongoStore() {
|
||||
return mongoStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoStoreInvocationContext getInvocationContext() {
|
||||
return invocationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface MongoConnectionProvider extends Provider {
|
||||
|
||||
DB getDB();
|
||||
|
||||
MongoStore getMongoStore();
|
||||
|
||||
MongoStoreInvocationContext getInvocationContext();
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsMongo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return MongoConnectionProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return MongoConnectionProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-3
@@ -1,7 +1,9 @@
|
||||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
@@ -35,7 +37,11 @@ public class MongoKeycloakTransaction implements KeycloakTransaction {
|
||||
throw new IllegalStateException("Can't commit as transaction marked for rollback");
|
||||
}
|
||||
|
||||
invocationContext.commit();
|
||||
try {
|
||||
invocationContext.commit();
|
||||
} catch (MongoException e) {
|
||||
throw MongoStoreImpl.convertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
/**
|
||||
* Base interface for object, which is persisted in Mongo
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* Entity with Id
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Target({TYPE})
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Inherited
|
||||
public @interface MongoIndex {
|
||||
|
||||
String[] fields();
|
||||
|
||||
boolean unique() default false;
|
||||
|
||||
boolean sparse() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Target({TYPE})
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Inherited
|
||||
public @interface MongoIndexes {
|
||||
|
||||
MongoIndex[] value();
|
||||
|
||||
}
|
||||
+7
-2
@@ -1,7 +1,7 @@
|
||||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,6 +31,10 @@ public interface MongoStore {
|
||||
|
||||
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
|
||||
|
||||
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context);
|
||||
|
||||
<T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
|
||||
|
||||
boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
|
||||
|
||||
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
|
||||
@@ -40,4 +44,5 @@ public interface MongoStore {
|
||||
<S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
|
||||
|
||||
<S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package org.keycloak.models.mongo.api.context;
|
||||
package org.keycloak.connections.mongo.api.context;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
|
||||
/**
|
||||
* Context, which provides callback methods to be invoked by MongoStore
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api.context;
|
||||
package org.keycloak.connections.mongo.api.context;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
/**
|
||||
* SPI object to convert object from application type to database type and vice versa. Shouldn't be directly used by application.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.keycloak.connections.mongo.impl;
|
||||
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class EntityInfo {
|
||||
|
||||
private final Class<?> entityClass;
|
||||
|
||||
private final String dbCollectionName;
|
||||
|
||||
private final Map<String, Property<Object>> properties;
|
||||
|
||||
public EntityInfo(Class<?> entityClass, String dbCollectionName, Map<String, Property<Object>> properties) {
|
||||
this.entityClass = entityClass;
|
||||
this.dbCollectionName = dbCollectionName;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
public String getDbCollectionName() {
|
||||
return dbCollectionName;
|
||||
}
|
||||
|
||||
public Collection<Property<Object>> getProperties() {
|
||||
return properties.values();
|
||||
}
|
||||
|
||||
public Property<Object> getPropertyByName(String propertyName) {
|
||||
return properties.get(propertyName);
|
||||
}
|
||||
}
|
||||
+132
-40
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.models.mongo.impl;
|
||||
package org.keycloak.connections.mongo.impl;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -6,30 +6,33 @@ import com.mongodb.DB;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBListMapper;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBObjectMapper;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBObjectToMapMapper;
|
||||
import org.keycloak.models.mongo.impl.types.EnumToStringMapper;
|
||||
import org.keycloak.models.mongo.impl.types.ListMapper;
|
||||
import org.keycloak.models.mongo.impl.types.MapMapper;
|
||||
import org.keycloak.models.mongo.impl.types.MongoEntityMapper;
|
||||
import org.keycloak.models.mongo.impl.types.SimpleMapper;
|
||||
import org.keycloak.models.mongo.impl.types.StringToEnumMapper;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.MongoIndexes;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
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 org.keycloak.connections.mongo.impl.types.BasicDBListMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.BasicDBObjectMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.BasicDBObjectToMapMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.EnumToStringMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.ListMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.MapMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.MongoEntityMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.SimpleMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.StringToEnumMapper;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
|
||||
import org.picketlink.common.properties.query.PropertyQueries;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.PropertyQueries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -50,22 +53,22 @@ public class MongoStoreImpl implements MongoStore {
|
||||
private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
|
||||
|
||||
private final MapperRegistry mapperRegistry;
|
||||
private ConcurrentMap<Class<? extends MongoEntity>, EntityInfo> entityInfoCache =
|
||||
new ConcurrentHashMap<Class<? extends MongoEntity>, EntityInfo>();
|
||||
private ConcurrentMap<Class<?>, EntityInfo> entityInfoCache =
|
||||
new ConcurrentHashMap<Class<?>, EntityInfo>();
|
||||
|
||||
|
||||
public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<? extends MongoEntity>[] managedEntityTypes) {
|
||||
public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<?>[] managedEntityTypes) {
|
||||
this.database = database;
|
||||
|
||||
mapperRegistry = new MapperRegistry();
|
||||
|
||||
for (Class<?> simpleConverterClass : SIMPLE_TYPES) {
|
||||
SimpleMapper converter = new SimpleMapper(simpleConverterClass);
|
||||
mapperRegistry.addAppObjectMapper(converter);
|
||||
mapperRegistry.addDBObjectMapper(converter);
|
||||
for (Class<?> simpleMapperClass : SIMPLE_TYPES) {
|
||||
SimpleMapper mapper = new SimpleMapper(simpleMapperClass);
|
||||
mapperRegistry.addAppObjectMapper(mapper);
|
||||
mapperRegistry.addDBObjectMapper(mapper);
|
||||
}
|
||||
|
||||
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list impl will be ArrayList)
|
||||
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list idm will be ArrayList)
|
||||
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, ArrayList.class));
|
||||
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, List.class));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBListMapper(mapperRegistry));
|
||||
@@ -78,7 +81,7 @@ public class MongoStoreImpl implements MongoStore {
|
||||
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
|
||||
mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
|
||||
|
||||
for (Class<? extends MongoEntity> type : managedEntityTypes) {
|
||||
for (Class<?> type : managedEntityTypes) {
|
||||
getEntityInfo(type);
|
||||
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
|
||||
@@ -88,6 +91,8 @@ public class MongoStoreImpl implements MongoStore {
|
||||
// dropDatabase();
|
||||
clearManagedCollections(managedEntityTypes);
|
||||
}
|
||||
|
||||
initManagedCollections(managedEntityTypes);
|
||||
}
|
||||
|
||||
protected void dropDatabase() {
|
||||
@@ -95,9 +100,9 @@ public class MongoStoreImpl implements MongoStore {
|
||||
logger.info("Database " + this.database.getName() + " dropped in MongoDB");
|
||||
}
|
||||
|
||||
// Don't drop database, but just clear all data in managed collections (useful for development)
|
||||
protected void clearManagedCollections(Class<? extends MongoEntity>[] managedEntityTypes) {
|
||||
for (Class<? extends MongoEntity> clazz : managedEntityTypes) {
|
||||
// Don't drop database, but just clear all data in managed collections (useful for export/import or during development)
|
||||
protected void clearManagedCollections(Class<?>[] managedEntityTypes) {
|
||||
for (Class<?> clazz : managedEntityTypes) {
|
||||
DBCollection dbCollection = getDBCollectionForType(clazz);
|
||||
if (dbCollection != null) {
|
||||
dbCollection.remove(new BasicDBObject());
|
||||
@@ -106,6 +111,52 @@ public class MongoStoreImpl implements MongoStore {
|
||||
}
|
||||
}
|
||||
|
||||
protected void initManagedCollections(Class<?>[] managedEntityTypes) {
|
||||
for (Class<?> clazz : managedEntityTypes) {
|
||||
EntityInfo entityInfo = getEntityInfo(clazz);
|
||||
String dbCollectionName = entityInfo.getDbCollectionName();
|
||||
if (dbCollectionName != null && !database.collectionExists(dbCollectionName)) {
|
||||
DBCollection dbCollection = database.getCollection(dbCollectionName);
|
||||
|
||||
logger.debug("Created collection " + dbCollection.getName() + " in " + this.database.getName());
|
||||
|
||||
MongoIndex index = clazz.getAnnotation(MongoIndex.class);
|
||||
if (index != null) {
|
||||
createIndex(dbCollection, index);
|
||||
}
|
||||
|
||||
MongoIndexes indexes = clazz.getAnnotation(MongoIndexes.class);
|
||||
if (indexes != null) {
|
||||
for (MongoIndex i : indexes.value()) {
|
||||
createIndex(dbCollection, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createIndex(DBCollection dbCollection, MongoIndex index) {
|
||||
BasicDBObject fields = new BasicDBObject();
|
||||
for (String f : index.fields()) {
|
||||
fields.put(f, 1);
|
||||
}
|
||||
|
||||
boolean unique = index.unique();
|
||||
boolean sparse = index.sparse();
|
||||
|
||||
BasicDBObject options = new BasicDBObject();
|
||||
if (unique) {
|
||||
options.put("unique", unique);
|
||||
}
|
||||
if (sparse) {
|
||||
options.put("sparse", sparse);
|
||||
}
|
||||
|
||||
dbCollection.ensureIndex(fields, options);
|
||||
|
||||
logger.debug("Created index " + fields + "(options: " + options + ") on " + dbCollection.getName() + " in " + this.database.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
|
||||
Class<? extends MongoEntity> clazz = entity.getClass();
|
||||
@@ -129,12 +180,24 @@ public class MongoStoreImpl implements MongoStore {
|
||||
// Adding "_id"
|
||||
dbObject.put("_id", currentId);
|
||||
|
||||
dbCollection.insert(dbObject);
|
||||
try {
|
||||
dbCollection.insert(dbObject);
|
||||
} catch (MongoException e) {
|
||||
throw convertException(e);
|
||||
}
|
||||
|
||||
// Treat object as created in this transaction (It is already submited to transaction)
|
||||
context.addCreatedEntity(entity);
|
||||
}
|
||||
|
||||
public static ModelException convertException(MongoException e) {
|
||||
if (e instanceof MongoException.DuplicateKey) {
|
||||
return new ModelDuplicateException(e);
|
||||
} else {
|
||||
return new ModelException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
|
||||
MongoTask fullUpdateTask = new MongoTask() {
|
||||
@@ -171,7 +234,7 @@ public class MongoStoreImpl implements MongoStore {
|
||||
public <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context) {
|
||||
// First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
|
||||
T cached = context.getLoadedEntity(type, id);
|
||||
if (cached != null) return cached;
|
||||
if (cached != null && type.isAssignableFrom(cached.getClass())) return cached;
|
||||
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
|
||||
@@ -217,6 +280,35 @@ public class MongoStoreImpl implements MongoStore {
|
||||
return convertCursor(type, cursor, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context) {
|
||||
// First we should execute all pending tasks before searching DB
|
||||
context.beforeDBSearch(type);
|
||||
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
DBCursor cursor = dbCollection.find(query);
|
||||
if (firstResult != -1) {
|
||||
cursor.skip(firstResult);
|
||||
}
|
||||
if (maxResults != -1) {
|
||||
cursor.limit(maxResults);
|
||||
}
|
||||
if (sort != null) {
|
||||
cursor.sort(sort);
|
||||
}
|
||||
|
||||
return convertCursor(type, cursor, context);
|
||||
}
|
||||
|
||||
public <T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
|
||||
context.beforeDBSearch(type);
|
||||
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
Long count = dbCollection.count(query);
|
||||
|
||||
// For now, assume that int is sufficient
|
||||
return count.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
|
||||
@@ -360,10 +452,10 @@ public class MongoStoreImpl implements MongoStore {
|
||||
mapperRegistry.addDBObjectMapper(mapper);
|
||||
}
|
||||
|
||||
public EntityInfo getEntityInfo(Class<? extends MongoEntity> entityClass) {
|
||||
public EntityInfo getEntityInfo(Class<?> entityClass) {
|
||||
EntityInfo entityInfo = entityInfoCache.get(entityClass);
|
||||
if (entityInfo == null) {
|
||||
List<Property<Object>> properties = PropertyQueries.createQuery(entityClass).addCriteria(new AnnotatedPropertyCriteria(MongoField.class)).getResultList();
|
||||
Map<String, Property<Object>> properties = PropertyQueries.createQuery(entityClass).getWritableResultList();
|
||||
|
||||
MongoCollection classAnnotation = entityClass.getAnnotation(MongoCollection.class);
|
||||
|
||||
@@ -408,7 +500,7 @@ public class MongoStoreImpl implements MongoStore {
|
||||
return object;
|
||||
}
|
||||
|
||||
protected DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
|
||||
protected DBCollection getDBCollectionForType(Class<?> type) {
|
||||
EntityInfo entityInfo = getEntityInfo(type);
|
||||
String dbCollectionName = entityInfo.getDbCollectionName();
|
||||
return dbCollectionName==null ? null : database.getCollection(dbCollectionName);
|
||||
+5
-6
@@ -1,10 +1,9 @@
|
||||
package org.keycloak.models.mongo.impl.context;
|
||||
package org.keycloak.connections.mongo.impl.context;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
|
||||
/**
|
||||
* Context, which is not doing any postponing of tasks and does not cache anything
|
||||
+6
-6
@@ -1,4 +1,9 @@
|
||||
package org.keycloak.models.mongo.impl.context;
|
||||
package org.keycloak.connections.mongo.impl.context;
|
||||
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -6,11 +11,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
|
||||
/**
|
||||
* Invocation context, which has some very basic support for transactions, and is able to cache loaded objects.
|
||||
* It always execute all pending update tasks before start searching for other objects
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
+14
-15
@@ -1,26 +1,25 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
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 org.keycloak.connections.mongo.impl.EntityInfo;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.Types;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.picketlink.common.reflection.Types;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class BasicDBObjectMapper<S extends MongoEntity> implements Mapper<BasicDBObject, S> {
|
||||
public class BasicDBObjectMapper<S> implements Mapper<BasicDBObject, S> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(BasicDBObjectMapper.class);
|
||||
|
||||
@@ -73,7 +72,7 @@ public class BasicDBObjectMapper<S extends MongoEntity> implements Mapper<BasicD
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void setPropertyValue(MongoEntity entity, Object valueFromDB, Property property) {
|
||||
private void setPropertyValue(Object entity, Object valueFromDB, Property property) {
|
||||
if (valueFromDB == null) {
|
||||
property.setValue(entity, null);
|
||||
return;
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
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 java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* For now, there is support just for convert to Map<String, String>
|
||||
*
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
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.List;
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
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 java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* For now, we support just convert from Map<String, String>
|
||||
*
|
||||
+17
-12
@@ -1,20 +1,20 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
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 org.keycloak.connections.mongo.impl.EntityInfo;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, BasicDBObject> {
|
||||
public class MongoEntityMapper<T> implements Mapper<T, BasicDBObject> {
|
||||
|
||||
private final MongoStoreImpl mongoStoreImpl;
|
||||
private final MapperRegistry mapperRegistry;
|
||||
@@ -37,10 +37,15 @@ public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, Basic
|
||||
Collection<Property<Object>> props = entityInfo.getProperties();
|
||||
for (Property<Object> property : props) {
|
||||
String propName = property.getName();
|
||||
Object propValue = property.getValue(applicationObject);
|
||||
|
||||
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
|
||||
dbObject.put(propName, dbValue);
|
||||
// Ignore "id" property
|
||||
if (!"id".equals(propName) || !(applicationObject instanceof MongoIdentifiableEntity)) {
|
||||
Object propValue = property.getValue(applicationObject);
|
||||
if (propValue != null) {
|
||||
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
|
||||
dbObject.put(propName, dbValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dbObject;
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* Just returns input
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.mongo.DefaultMongoConnectionFactoryProvider
|
||||
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.mongo.MongoConnectionSpi
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4</version>
|
||||
</parent>
|
||||
<name>Connections Parent</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>jpa</module>
|
||||
<module>mongo</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+5
-9
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-alpha-3</version>
|
||||
<version>1.0-beta-4</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -18,11 +19,6 @@
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
@@ -61,8 +57,8 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
+5
-4
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-alpha-3</version>
|
||||
<version>1.0-beta-4</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -45,8 +46,8 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.keycloak;
|
||||
|
||||
import org.keycloak.util.KeycloakUriBuilder;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -12,19 +11,20 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AbstractOAuthClient {
|
||||
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
|
||||
private static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
|
||||
protected String clientId;
|
||||
protected Map<String, String> credentials;
|
||||
protected String authUrl;
|
||||
protected String codeUrl;
|
||||
protected String refreshUrl;
|
||||
protected boolean relativeUrls;
|
||||
protected String scope;
|
||||
protected String stateCookieName = OAUTH_TOKEN_REQUEST_STATE;
|
||||
protected String stateCookiePath;
|
||||
protected boolean isSecure;
|
||||
protected boolean publicClient;
|
||||
protected final AtomicLong counter = new AtomicLong();
|
||||
|
||||
protected String getStateCode() {
|
||||
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
||||
}
|
||||
@@ -101,10 +101,18 @@ public class AbstractOAuthClient {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public boolean isRelativeUrls() {
|
||||
return relativeUrls;
|
||||
}
|
||||
|
||||
public void setRelativeUrls(boolean relativeUrls) {
|
||||
this.relativeUrls = relativeUrls;
|
||||
}
|
||||
|
||||
protected String stripOauthParametersFromRedirect(String uri) {
|
||||
KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri)
|
||||
.replaceQueryParam("code", null)
|
||||
.replaceQueryParam("state", null);
|
||||
.replaceQueryParam(OAuth2Constants.CODE, null)
|
||||
.replaceQueryParam(OAuth2Constants.STATE, null);
|
||||
return builder.build().toString();
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package org.keycloak;
|
||||
|
||||
/**
|
||||
* Information about the client connection
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientConnection {
|
||||
String getRemoteAddr();
|
||||
String getRemoteHost();
|
||||
int getReportPort();
|
||||
}
|
||||
Executable
+150
@@ -0,0 +1,150 @@
|
||||
package org.keycloak;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
private static ConfigProvider configProvider = new SystemPropertiesConfigProvider();
|
||||
|
||||
public static void init(ConfigProvider configProvider) {
|
||||
Config.configProvider = configProvider;
|
||||
}
|
||||
|
||||
public static String getAdminRealm() {
|
||||
return configProvider.scope("admin").get("realm", "master");
|
||||
}
|
||||
|
||||
public static String getProvider(String spi) {
|
||||
String provider = configProvider.getProvider(spi);
|
||||
if (provider == null || provider.trim().equals("")) {
|
||||
return null;
|
||||
} else {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
public static Scope scope(String... scope) {
|
||||
return configProvider.scope(scope);
|
||||
}
|
||||
|
||||
public static interface ConfigProvider {
|
||||
|
||||
String getProvider(String spi);
|
||||
|
||||
Scope scope(String... scope);
|
||||
|
||||
}
|
||||
|
||||
public static class SystemPropertiesConfigProvider implements ConfigProvider {
|
||||
|
||||
@Override
|
||||
public String getProvider(String spi) {
|
||||
return System.getProperties().getProperty("keycloak." + spi + ".provider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope scope(String... scope) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("keycloak.");
|
||||
for (String s : scope) {
|
||||
sb.append(s);
|
||||
sb.append(".");
|
||||
}
|
||||
return new SystemPropertiesScope(sb.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SystemPropertiesScope implements Scope {
|
||||
|
||||
private String prefix;
|
||||
|
||||
public SystemPropertiesScope(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return get(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String key, String defaultValue) {
|
||||
return System.getProperty(prefix + key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getArray(String key) {
|
||||
String value = get(key);
|
||||
if (value != null) {
|
||||
String[] a = value.split(",");
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
a[i] = a[i].trim();
|
||||
}
|
||||
return a;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt(String key) {
|
||||
return getInt(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt(String key, Integer defaultValue) {
|
||||
String v = get(key, null);
|
||||
return v != null ? Integer.parseInt(v) : defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key) {
|
||||
return getLong(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
String v = get(key, null);
|
||||
return v != null ? Long.parseLong(v) : defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBoolean(String key) {
|
||||
return getBoolean(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
String v = get(key, null);
|
||||
return v != null ? Boolean.parseBoolean(v) : defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public static interface Scope {
|
||||
|
||||
String get(String key);
|
||||
|
||||
String get(String key, String defaultValue);
|
||||
|
||||
String[] getArray(String key);
|
||||
|
||||
Integer getInt(String key);
|
||||
|
||||
Integer getInt(String key, Integer defaultValue);
|
||||
|
||||
Long getLong(String key);
|
||||
|
||||
Long getLong(String key, Long defaultValue);
|
||||
|
||||
Boolean getBoolean(String key);
|
||||
|
||||
Boolean getBoolean(String key, Boolean defaultValue);
|
||||
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1429 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user