Compare commits
1001
Commits
1.0-alpha-3
...
1.0-rc-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1199e26a65 | ||
|
|
ff74d1ddc0 | ||
|
|
31b0f5543f | ||
|
|
39c0be5d99 | ||
|
|
fb18789b05 | ||
|
|
bcbf5da4b8 | ||
|
|
e1b9dfe65d | ||
|
|
52f86f2293 | ||
|
|
e413694cf5 | ||
|
|
20ce36dc64 | ||
|
|
a3c982a630 | ||
|
|
e0542b5260 | ||
|
|
7c7025a83d | ||
|
|
3e6a1f47ec | ||
|
|
32703d19a0 | ||
|
|
cdc6588ac1 | ||
|
|
57aea9b51c | ||
|
|
a2171bf777 | ||
|
|
b9d0f64f74 | ||
|
|
55d614fce8 | ||
|
|
46d118207a | ||
|
|
3bfe3d256e | ||
|
|
b50ab14a0a | ||
|
|
08c58cf2f2 | ||
|
|
d26543360b | ||
|
|
fb18004c1d | ||
|
|
49fb452a79 | ||
|
|
55bd889a0f | ||
|
|
269a0dbec1 | ||
|
|
cf51db88e7 | ||
|
|
fc0889cd2e | ||
|
|
b3b480c25f | ||
|
|
892fa04130 | ||
|
|
8d3e51c236 | ||
|
|
fd9d2ba4d5 | ||
|
|
75bc633a33 | ||
|
|
3fdb22cb05 | ||
|
|
f51ba1cb8a | ||
|
|
688f4a07a3 | ||
|
|
6c7d0f27a7 | ||
|
|
1e5ca3062c | ||
|
|
ca97e7ba35 | ||
|
|
e99afec7ec | ||
|
|
a23853a6bc | ||
|
|
7c8edb5d11 | ||
|
|
83ad045410 | ||
|
|
e5d5276135 | ||
|
|
6f38a02aaa | ||
|
|
5dd84f1d9a | ||
|
|
30147ce25a | ||
|
|
dd011f043d | ||
|
|
7dec0319b6 | ||
|
|
06c14df0f3 | ||
|
|
041d0be990 | ||
|
|
f6a76e673c | ||
|
|
47f361a4ab | ||
|
|
6e7924da76 | ||
|
|
df1a0c03b6 | ||
|
|
971bea8a24 | ||
|
|
c8f0a1ab5a | ||
|
|
0ae9ac0848 | ||
|
|
3dd52e8b28 | ||
|
|
0f6915c615 | ||
|
|
74cd23b331 | ||
|
|
cc17895ef3 | ||
|
|
09ada81b27 | ||
|
|
75e0fa87c3 | ||
|
|
6628e226e4 | ||
|
|
89d47a392b | ||
|
|
c38abb2cfe | ||
|
|
faf18dfdfd | ||
|
|
2dc32fb8c9 | ||
|
|
2ec029e8e0 | ||
|
|
80904209b3 | ||
|
|
8b1483d7ef | ||
|
|
bbeff1ef22 | ||
|
|
aac3c2e0ee | ||
|
|
a240a00983 | ||
|
|
c86a6ba3f3 | ||
|
|
bc301baa1f | ||
|
|
edbf26f007 | ||
|
|
5dca2a3588 | ||
|
|
cf5a7d228b | ||
|
|
f7a7c3ca7b | ||
|
|
28fcdf0b8f | ||
|
|
c38ec33881 | ||
|
|
cfee00d4c5 | ||
|
|
46bf0d78fa | ||
|
|
a14be592de | ||
|
|
29691a3167 | ||
|
|
517a2b99d7 | ||
|
|
edf73af508 | ||
|
|
4a5ea08a2a | ||
|
|
7b86cca649 | ||
|
|
c77b7cd7d6 | ||
|
|
6b0a87c57a | ||
|
|
b1c331c5f3 | ||
|
|
7ae342452e | ||
|
|
4438aa6606 | ||
|
|
8cd8472003 | ||
|
|
687aa78567 | ||
|
|
8419e5883d | ||
|
|
4bac0474c6 | ||
|
|
f0b66a2241 | ||
|
|
51b71e8f20 | ||
|
|
8615515b9a | ||
|
|
10cf486d26 | ||
|
|
abb1647672 | ||
|
|
114c1b95fa | ||
|
|
4e88bbc811 | ||
|
|
504a41aec4 | ||
|
|
e9bf1ef618 | ||
|
|
9d6e450c05 | ||
|
|
fb7e95f1e4 | ||
|
|
f95418dfc9 | ||
|
|
94be235a0f | ||
|
|
100de5a55e | ||
|
|
26570de4cd | ||
|
|
7c97e02715 | ||
|
|
e73bdf9d8c | ||
|
|
8d1ff1d49c | ||
|
|
de770d2881 | ||
|
|
7f96770089 | ||
|
|
3f83538fc1 | ||
|
|
3040f4e89f | ||
|
|
c57c264d91 | ||
|
|
8ba7559cac | ||
|
|
c4d5a4138c | ||
|
|
d699404ce7 | ||
|
|
ee79747cb6 | ||
|
|
5cb6c4e77c | ||
|
|
05638a8953 | ||
|
|
48f7efc97f | ||
|
|
9aa6b4cbfa | ||
|
|
cae0a7628d | ||
|
|
82e29dfcb3 | ||
|
|
ad94b94833 | ||
|
|
687e497455 | ||
|
|
67f31648a6 | ||
|
|
d332851a75 | ||
|
|
4a18a32cfc | ||
|
|
428cd54a81 | ||
|
|
dc4e8603d7 | ||
|
|
b96f8aef6d | ||
|
|
dc50c312cb | ||
|
|
1f12a2d67b | ||
|
|
a2bef7dbbe | ||
|
|
c6af13c8cc | ||
|
|
5be5f6475f | ||
|
|
5673fb34b1 | ||
|
|
5214fe2a55 | ||
|
|
76db33829b | ||
|
|
746da8f270 | ||
|
|
f8667cdb38 | ||
|
|
1ec4fe921d | ||
|
|
915701106c | ||
|
|
2309e6f040 | ||
|
|
839d823edd | ||
|
|
99e6f0edad | ||
|
|
bb138bd6e4 | ||
|
|
32a19563a5 | ||
|
|
3b65127399 | ||
|
|
c17a5823e9 | ||
|
|
c9e88c944a | ||
|
|
b93cf4868c | ||
|
|
53ac88ca26 | ||
|
|
aff32f69bb | ||
|
|
4fcf11d0db | ||
|
|
e04af84ac0 | ||
|
|
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 | ||
|
|
9e4086e501 | ||
|
|
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 | ||
|
|
5764ad84e9 | ||
|
|
f3fde4a8df | ||
|
|
983ccdd121 | ||
|
|
73ed22c52c | ||
|
|
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,4 +1,35 @@
|
||||
keycloak
|
||||
========
|
||||
|
||||
SSO Service for web apps and REST services.
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
|
||||
Keycloak is an SSO Service for web apps and REST services.
|
||||
It can be used for social applications as well as enterprise applications. It is based on OpenID Connect with SAML support in the works.
|
||||
Here's some of the features:
|
||||
|
||||
* SSO and Single Log Out for browser applications
|
||||
* Social Broker. Enable Google, Facebook, Yahoo, Twitter social login with no code required.
|
||||
* Optional LDAP/Active Directory integration
|
||||
* Optional User Registration
|
||||
* Password and TOTP support (via Google Authenticator). Client cert auth coming soon.
|
||||
* User session management from both admin and user perspective
|
||||
* Customizable themes for user facing pages: login, grant pages, account management, emails, and admin console all customizable!
|
||||
* OAuth Bearer token auth for REST Services
|
||||
* Integrated Browser App to REST Service token propagation
|
||||
* Admin REST API
|
||||
* OAuth 2.0 Grant requests
|
||||
* CORS Support
|
||||
* CORS Web Origin management and validation
|
||||
* Completely centrally managed user and role mapping metadata. Minimal configuration at the application side
|
||||
* Admin Console for managing users, roles, role mappings, applications, user sessions, allowed CORS web origins, and OAuth clients.
|
||||
* Deployable as a WAR, appliance, or an Openshift cloud service (SaaS).
|
||||
* Supports JBoss AS7, EAP 6.x, and Wildfly applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java applications.
|
||||
* Javascript/HTML 5 adapter for pure Javascript apps
|
||||
* Session management from admin console
|
||||
* Revocation policies
|
||||
* Password policies
|
||||
* OpenID Connect Support
|
||||
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">Settings</a></li>
|
||||
<li data-ng-show="!application.bearerOnly && !application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/credentials">Credentials</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/installation">Installation</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/roles">Roles</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/claims">Claims</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/scope-mappings">Scope</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/revocation">Revocation</a></li>
|
||||
<li data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.name}}/sessions">Sessions</a></li>
|
||||
</ul>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-show="create">
|
||||
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li class="active">Add Application</li>
|
||||
</ol>
|
||||
<h2 data-ng-show="create"><span>{{realm.realm}}</span> Add Application</h2>
|
||||
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/applications/{{application.name}}">{{application.name}}</a></li>
|
||||
<li class="active">Settings</li>
|
||||
</ol>
|
||||
<h2 data-ng-hide="create"><span>{{application.name}}</span> Settings</h2>
|
||||
<form class="form-horizontal" name="applicationForm" novalidate kc-read-only="!access.manageApplications">
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="application.name" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group clearfix block">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="application.enabled" name="enabled" id="enabled" onoffswitch />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="clientType">Client Type</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="select-kc">
|
||||
<select id="clientType"
|
||||
ng-change="changeClientType()"
|
||||
ng-model="clientType"
|
||||
ng-options="cType for cType in clientTypes">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="!application.bearerOnly">
|
||||
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
|
||||
<div class="col-sm-6">
|
||||
<div ng-repeat="redirectUri in application.redirectUris" class="row kc-item-deletable">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
|
||||
name="redirectUri" id="redirectUri" data-ng-model="redirectUri" readonly />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-danger" type="button" data-ng-click="deleteRedirectUri($index)">
|
||||
Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="newRedirectUri" id="newRedirectUri"
|
||||
placeholder="New Redirect URI..." data-ng-model="newRedirectUri"
|
||||
data-ng-class="{'input-below':application.redirectUris.length}" />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-primary" data-ng-click="addRedirectUri()" ng-show="newRedirectUri.length > 0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="baseUrl">Base URL</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" name="baseUrl" id="baseUrl"
|
||||
data-ng-model="application.baseUrl">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="adminUrl">Admin URL</label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" name="adminUrl" id="adminUrl"
|
||||
data-ng-model="application.adminUrl">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
|
||||
<div class="col-sm-6">
|
||||
<div ng-repeat="webOrigin in application.webOrigins" class="row kc-item-deletable">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control kc-button-control"
|
||||
type="text" data-ng-class="{'input-below':!$first}"
|
||||
name="webOrigin" id="webOrigin"
|
||||
data-ng-model="webOrigin" readonly/>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-danger"
|
||||
type="button" data-ng-click="deleteWebOrigin($index)">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control kc-button-control"
|
||||
type="text" name="newWebOrigin" id="newWebOrigin"
|
||||
placeholder="New Web Origin..." data-ng-model="newWebOrigin"
|
||||
data-ng-class="{'input-below':application.webOrigins.length}"/>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-primary"
|
||||
data-ng-click="addWebOrigin()" ng-show="newWebOrigin.length > 0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="create && access.manageApplications">
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
<div class="pull-right form-actions" data-ng-show="!create && access.manageApplications">
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">Settings</a></li>
|
||||
<li data-ng-show="!oauth.publicClient"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
|
||||
</ul>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-show="create">
|
||||
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
|
||||
<li class="active">Add OAuth Client</li>
|
||||
</ol>
|
||||
<h2 data-ng-show="create"><span>{{realm.realm}}</span> Add OAuth Client</h2>
|
||||
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
|
||||
<li class="active">Settings</li>
|
||||
</ol>
|
||||
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Settings</h2>
|
||||
<form class="form-horizontal" name="oauthForm" novalidate kc-read-only="!access.manageClients">
|
||||
<fieldset class="border-top">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="oauth.name" autofocus
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group clearfix block">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="oauth.enabled" name="enabled" id="enabled" onoffswitch />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="clientType">Client Type</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="select-kc">
|
||||
<select id="clientType"
|
||||
ng-change="changeClientType()"
|
||||
ng-model="clientType"
|
||||
ng-options="cType for cType in clientTypes">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
|
||||
<div class="col-sm-6">
|
||||
<div ng-repeat="redirectUri in oauth.redirectUris" class="kc-item-deletable row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
|
||||
name="redirectUri" id="redirectUri" data-ng-model="redirectUri" readonly />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-danger" type="button" data-ng-click="deleteRedirectUri($index)">
|
||||
Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="newRedirectUri" id="newRedirectUri"
|
||||
placeholder="New Redirect URI..." data-ng-model="newRedirectUri"
|
||||
data-ng-class="{'input-below':oauth.redirectUris.length}" />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-primary" data-ng-click="addRedirectUri()" ng-show="newRedirectUri.length > 0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
|
||||
<div class="col-sm-6">
|
||||
<div ng-repeat="webOrigin in oauth.webOrigins" class="kc-item-deletable row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" data-ng-class="{'input-below':!$first}"
|
||||
name="webOrigin" id="webOrigin" data-ng-model="webOrigin" readonly />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-danger" type="button" data-ng-click="deleteWebOrigin($index)">
|
||||
Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control" type="text" name="newWebOrigin" id="newWebOrigin"
|
||||
placeholder="New Web Origin..." data-ng-model="newWebOrigin"
|
||||
data-ng-class="{'input-below':oauth.webOrigins.length}" />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button class="btn btn-primary" data-ng-click="addWebOrigin()" ng-show="newWebOrigin.length > 0">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="pull-right form-actions" data-ng-show="create && access.manageClients">
|
||||
<button kc-cancel data-ng-click="cancel()">Cancel</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
</div>
|
||||
<div class="pull-right form-actions" data-ng-show="!create && access.manageClients">
|
||||
<button kc-reset data-ng-show="changed">Clear changes</button>
|
||||
<button kc-save data-ng-show="changed">Save</button>
|
||||
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,10 +0,0 @@
|
||||
<ul data-ng-hide="createRealm">
|
||||
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' ||
|
||||
path[2] == 'social-settings' || path[2] == 'required-credentials' || path[2] == 'default-roles' || path[2] == 'registration-settings' ||
|
||||
path[2] == 'keys-settings' || path[2] == 'smtp-settings') && path[3] != 'applications') && 'active'"><a href="#/realms/{{realm.realm}}">Settings</a></li>
|
||||
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users">Users</a>
|
||||
</li>
|
||||
<li data-ng-show="access.viewApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
|
||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/revocation">Sessions</a></li>
|
||||
</ul>
|
||||
@@ -1,47 +0,0 @@
|
||||
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
|
||||
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
|
||||
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
|
||||
</ul>
|
||||
|
||||
<div id="content">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
|
||||
<li class="active">Users</li>
|
||||
</ol>
|
||||
<h2><span>{{user.username}}'s</span> Credentials</h2>
|
||||
|
||||
<form class="form-horizontal" name="userForm" novalidate>
|
||||
<fieldset class="border-top">
|
||||
<legend><span class="text">Credential Management</span></legend>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="password">Reset password</label>
|
||||
<div class="col-sm-4 kc-multiline">
|
||||
<input class="form-control" type="password" id="password" name="password" data-ng-model="password" placeholder="Temporary password" required>
|
||||
<input class="form-control" type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword" placeholder="Password confirmation" required>
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="resetPassword()" data-ng-show="password">Reset Password</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="user.email">
|
||||
<label class="col-sm-2 control-label" for="password">Reset password email</label>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="resetPasswordEmail()" >Send Email</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" data-ng-show="user.totp">
|
||||
<label class="col-sm-2 control-label" for="password">Remove totp</label>
|
||||
<div class="col-sm-4" data-ng-show="user.totp">
|
||||
<button class="btn btn-danger" type="submit" data-ng-click="removeTotp()">Remove TOTP</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -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,10 +0,0 @@
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
|
||||
<li ng-class="{active: path[2] == 'social'}" data-ng-show="realm.social && access.viewRealm"><a href="#/realms/{{realm.realm}}/social-settings">Social</a></li>
|
||||
<li ng-class="{active: path[2] == 'roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li>
|
||||
<li ng-class="{active: path[2] == 'default-roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/default-roles">Default Roles</a></li>
|
||||
<li ng-class="{active: path[2] == 'required-credentials'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/required-credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">Token</a></li>
|
||||
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
|
||||
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
|
||||
</ul>
|
||||
@@ -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
+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-rc-2</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();
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* @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 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 driverDialect = config.get("driverDialect");
|
||||
if (driverDialect != null && driverDialect.length() > 0) {
|
||||
properties.put("hibernate.dialect", driverDialect);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+17
-6
@@ -2,24 +2,35 @@
|
||||
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.RealmAttributeEntity</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.events.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-rc-2</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;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-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,12 +37,18 @@ 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);
|
||||
}
|
||||
started = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
invocationContext.rollback();
|
||||
started = false;
|
||||
}
|
||||
|
||||
@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-rc-2</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-rc-2</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>
|
||||
|
||||
+15
-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-rc-2</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -12,6 +13,10 @@
|
||||
<name>Keycloak Core</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
@@ -40,13 +45,19 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,16 @@ import java.security.Principal;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakPrincipal implements Principal, Serializable {
|
||||
protected String name;
|
||||
protected String surrogate;
|
||||
protected final String name;
|
||||
protected final KeycloakSecurityContext context;
|
||||
|
||||
public KeycloakPrincipal(String name, String surrogate) {
|
||||
public KeycloakPrincipal(String name, KeycloakSecurityContext context) {
|
||||
this.name = name;
|
||||
this.surrogate = surrogate;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -21,10 +25,6 @@ public class KeycloakPrincipal implements Principal, Serializable {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSurrogate() {
|
||||
return surrogate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -33,16 +33,13 @@ public class KeycloakPrincipal implements Principal, Serializable {
|
||||
KeycloakPrincipal that = (KeycloakPrincipal) o;
|
||||
|
||||
if (!name.equals(that.name)) return false;
|
||||
if (surrogate != null ? !surrogate.equals(that.surrogate) : that.surrogate != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + (surrogate != null ? surrogate.hashCode() : 0);
|
||||
return result;
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.keycloak;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface OAuth2Constants {
|
||||
|
||||
String CODE = "code";
|
||||
|
||||
String CLIENT_ID = "client_id";
|
||||
|
||||
String ERROR = "error";
|
||||
|
||||
String ERROR_DESCRIPTION = "error_description";
|
||||
|
||||
String REDIRECT_URI = "redirect_uri";
|
||||
|
||||
String SCOPE = "scope";
|
||||
|
||||
String STATE = "state";
|
||||
|
||||
String GRANT_TYPE = "grant_type";
|
||||
|
||||
String RESPONSE_TYPE = "response_type";
|
||||
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ public class OAuthErrorException extends Exception {
|
||||
}
|
||||
public OAuthErrorException(String error, String description, String message) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
this.description = description;
|
||||
}
|
||||
public OAuthErrorException(String error, String description) {
|
||||
super(description);
|
||||
|
||||
@@ -16,29 +16,27 @@ public class RSATokenVerifier {
|
||||
return verifyToken(tokenString, realmKey, realm, true);
|
||||
}
|
||||
|
||||
|
||||
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realm, boolean checkActive) throws VerificationException {
|
||||
JWSInput input = new JWSInput(tokenString);
|
||||
boolean verified = false;
|
||||
JWSInput input = null;
|
||||
try {
|
||||
verified = RSAProvider.verify(input, realmKey);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
input = new JWSInput(tokenString);
|
||||
} catch (Exception e) {
|
||||
throw new VerificationException("Couldn't parse token", e);
|
||||
}
|
||||
if (!verified) throw new VerificationException("Token signature not validated");
|
||||
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
|
||||
|
||||
AccessToken token = null;
|
||||
AccessToken token;
|
||||
try {
|
||||
token = input.readJsonContent(AccessToken.class);
|
||||
} catch (IOException e) {
|
||||
throw new VerificationException(e);
|
||||
throw new VerificationException("Couldn't parse token signature", e);
|
||||
}
|
||||
String user = token.getSubject();
|
||||
if (user == null) {
|
||||
throw new VerificationException("Token user was null");
|
||||
throw new VerificationException("Token user was null.");
|
||||
}
|
||||
if (!realm.equals(token.getAudience())) {
|
||||
throw new VerificationException("Token audience doesn't match domain");
|
||||
throw new VerificationException("Token audience doesn't match domain.");
|
||||
|
||||
}
|
||||
if (checkActive && !token.isActive()) {
|
||||
@@ -47,4 +45,12 @@ public class RSATokenVerifier {
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
|
||||
try {
|
||||
return RSAProvider.verify(input, realmKey);
|
||||
} catch (Exception e) {
|
||||
throw new VerificationException("Token signature not validated.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,12 @@ package org.keycloak;
|
||||
*/
|
||||
public interface ServiceUrlConstants {
|
||||
|
||||
public static final String TOKEN_SERVICE_LOGIN_PATH = "/rest/realms/{realm-name}/tokens/login";
|
||||
public static final String TOKEN_SERVICE_ACCESS_CODE_PATH = "/rest/realms/{realm-name}/tokens/access/codes";
|
||||
public static final String TOKEN_SERVICE_REFRESH_PATH = "/rest/realms/{realm-name}/tokens/refresh";
|
||||
public static final String TOKEN_SERVICE_LOGIN_PATH = "/realms/{realm-name}/tokens/login";
|
||||
public static final String TOKEN_SERVICE_ACCESS_CODE_PATH = "/realms/{realm-name}/tokens/access/codes";
|
||||
public static final String TOKEN_SERVICE_REFRESH_PATH = "/realms/{realm-name}/tokens/refresh";
|
||||
public static final String TOKEN_SERVICE_LOGOUT_PATH = "/realms/{realm-name}/tokens/logout";
|
||||
public static final String TOKEN_SERVICE_DIRECT_GRANT_PATH = "/realms/{realm-name}/tokens/grants/access";
|
||||
public static final String ACCOUNT_SERVICE_PATH = "/realms/{realm-name}/account";
|
||||
public static final String REALM_INFO_PATH = "/realms/{realm-name}";
|
||||
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
package org.keycloak;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class Version {
|
||||
public static String VERSION;
|
||||
public static String BUILD_TIME;
|
||||
public static final String UNKNOWN = "UNKNOWN";
|
||||
public static final Version SINGLETON = new Version();
|
||||
|
||||
private final String version = VERSION;
|
||||
private final String buildTime = BUILD_TIME;
|
||||
|
||||
static {
|
||||
Properties props = new Properties();
|
||||
InputStream is = Version.class.getResourceAsStream("/keycloak-version.properties");
|
||||
try {
|
||||
props.load(is);
|
||||
VERSION = props.getProperty("version");
|
||||
BUILD_TIME = props.getProperty("build-time");
|
||||
} catch (IOException e) {
|
||||
VERSION=UNKNOWN;
|
||||
BUILD_TIME=UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("version")
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@JsonProperty("build-time")
|
||||
public String getBuildTime() {
|
||||
return buildTime;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ public interface AdapterConstants {
|
||||
|
||||
// URL endpoints
|
||||
public static final String K_LOGOUT = "k_logout";
|
||||
public static final String K_VERSION = "k_version";
|
||||
public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
|
||||
public static final String K_GET_USER_STATS = "k_get_user_stats";
|
||||
public static final String K_GET_SESSION_STATS = "k_get_session_stats";
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.keycloak.enums;
|
||||
|
||||
import org.keycloak.ClientConnection;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public enum SslRequired {
|
||||
|
||||
ALL,
|
||||
EXTERNAL,
|
||||
NONE;
|
||||
|
||||
public boolean isRequired(ClientConnection connection) {
|
||||
return isRequired(connection.getRemoteAddr());
|
||||
}
|
||||
|
||||
public boolean isRequired(String address) {
|
||||
switch (this) {
|
||||
case ALL:
|
||||
return true;
|
||||
case NONE:
|
||||
return false;
|
||||
case EXTERNAL:
|
||||
return !isLocal(address);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLocal(String remoteAddress) {
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(remoteAddress);
|
||||
return inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isSiteLocalAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.keycloak.jose.jws;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.annotate.JsonSerialize;
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HMACProvider {
|
||||
public static String getJavaAlgorithm(Algorithm alg) {
|
||||
private static String getJavaAlgorithm(Algorithm alg) {
|
||||
switch (alg) {
|
||||
case HS256:
|
||||
return "HMACSHA256";
|
||||
@@ -28,7 +28,7 @@ public class HMACProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public static Mac getMAC(final Algorithm alg) {
|
||||
private static Mac getMAC(final Algorithm alg) {
|
||||
|
||||
try {
|
||||
return Mac.getInstance(getJavaAlgorithm(alg));
|
||||
|
||||
@@ -128,12 +128,12 @@ public class AccessToken extends IDToken {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken expiration(long expiration) {
|
||||
public AccessToken expiration(int expiration) {
|
||||
return (AccessToken) super.expiration(expiration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken notBefore(long notBefore) {
|
||||
public AccessToken notBefore(int notBefore) {
|
||||
return (AccessToken) super.notBefore(notBefore);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ public class AccessTokenResponse {
|
||||
@JsonProperty("not-before-policy")
|
||||
protected int notBeforePolicy;
|
||||
|
||||
@JsonProperty("session-state")
|
||||
protected String sessionState;
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
@@ -74,4 +77,12 @@ public class AccessTokenResponse {
|
||||
public void setNotBeforePolicy(int notBeforePolicy) {
|
||||
this.notBeforePolicy = notBeforePolicy;
|
||||
}
|
||||
|
||||
public String getSessionState() {
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
public void setSessionState(String sessionState) {
|
||||
this.sessionState = sessionState;
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,9 @@ public class IDToken extends JsonWebToken {
|
||||
@JsonProperty("claims_locales")
|
||||
protected String claimsLocales;
|
||||
|
||||
@JsonProperty("session_state")
|
||||
protected String sessionState;
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
@@ -303,4 +306,12 @@ public class IDToken extends JsonWebToken {
|
||||
public void setClaimsLocales(String claimsLocales) {
|
||||
this.claimsLocales = claimsLocales;
|
||||
}
|
||||
|
||||
public String getSessionState() {
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
public void setSessionState(String sessionState) {
|
||||
this.sessionState = sessionState;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.keycloak.representations;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -13,9 +14,9 @@ public class JsonWebToken implements Serializable {
|
||||
@JsonProperty("jti")
|
||||
protected String id;
|
||||
@JsonProperty("exp")
|
||||
protected long expiration;
|
||||
protected int expiration;
|
||||
@JsonProperty("nbf")
|
||||
protected long notBefore;
|
||||
protected int notBefore;
|
||||
@JsonProperty("iat")
|
||||
protected int issuedAt;
|
||||
@JsonProperty("iss")
|
||||
@@ -39,26 +40,25 @@ public class JsonWebToken implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
public long getExpiration() {
|
||||
public int getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public JsonWebToken expiration(long expiration) {
|
||||
public JsonWebToken expiration(int expiration) {
|
||||
this.expiration = expiration;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isExpired() {
|
||||
long time = System.currentTimeMillis() / 1000;
|
||||
return time > expiration;
|
||||
return Time.currentTime() > expiration;
|
||||
}
|
||||
|
||||
public long getNotBefore() {
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public JsonWebToken notBefore(long notBefore) {
|
||||
public JsonWebToken notBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
return this;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class JsonWebToken implements Serializable {
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isNotBefore() {
|
||||
return (System.currentTimeMillis() / 1000) >= notBefore;
|
||||
return Time.currentTime() >= notBefore;
|
||||
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public class JsonWebToken implements Serializable {
|
||||
*/
|
||||
@JsonIgnore
|
||||
public JsonWebToken issuedNow() {
|
||||
issuedAt = (int)(System.currentTimeMillis() / 1000);
|
||||
issuedAt = Time.currentTime();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.keycloak.representations;
|
||||
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class PasswordToken {
|
||||
|
||||
private String realm;
|
||||
private String user;
|
||||
private int timestamp;
|
||||
|
||||
public PasswordToken() {
|
||||
}
|
||||
|
||||
public PasswordToken(String realm, String user) {
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.timestamp = Time.currentTime();
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public class RefreshToken extends AccessToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copies issuer, subject, issuedFor, realmAccess, and resourceAccess
|
||||
* Deep copies issuer, subject, issuedFor, sessionState, realmAccess, and resourceAccess
|
||||
* from AccessToken.
|
||||
*
|
||||
* @param token
|
||||
@@ -23,6 +23,7 @@ public class RefreshToken extends AccessToken {
|
||||
this.issuer = token.issuer;
|
||||
this.subject = token.subject;
|
||||
this.issuedFor = token.issuedFor;
|
||||
this.sessionState = token.sessionState;
|
||||
if (token.realmAccess != null) {
|
||||
realmAccess = token.realmAccess.clone();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.keycloak.representations.adapters.action;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
/**
|
||||
* Posted to managed client from admin server.
|
||||
@@ -34,8 +35,7 @@ public abstract class AdminAction {
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isExpired() {
|
||||
long time = System.currentTimeMillis() / 1000;
|
||||
return time > expiration;
|
||||
return Time.currentTime() > expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,14 +7,17 @@ package org.keycloak.representations.adapters.action;
|
||||
public class LogoutAction extends AdminAction {
|
||||
public static final String LOGOUT = "LOGOUT";
|
||||
protected String user;
|
||||
private String session;
|
||||
protected int notBefore;
|
||||
|
||||
public LogoutAction() {
|
||||
}
|
||||
|
||||
public LogoutAction(String id, int expiration, String resource, String user, int notBefore) {
|
||||
public LogoutAction(String id, int expiration, String resource, String user, String session, int notBefore) {
|
||||
super(id, expiration, resource, LOGOUT);
|
||||
this.user = user;
|
||||
this.session = session;
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
@@ -25,6 +28,14 @@ public class LogoutAction extends AdminAction {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void setSession(String session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.keycloak.representations.adapters.action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
|
||||
"resource", "credentials",
|
||||
"use-resource-role-mappings",
|
||||
"enable-cors", "cors-max-age", "cors-allowed-methods",
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required",
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required",
|
||||
"resource", "public-client", "credentials",
|
||||
"use-resource-role-mappings",
|
||||
"enable-cors", "cors-max-age", "cors-allowed-methods",
|
||||
|
||||
+7
-7
@@ -9,7 +9,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-not-required"})
|
||||
@JsonPropertyOrder({"realm", "realm-public-key", "auth-server-url", "ssl-required"})
|
||||
public class BaseRealmConfig {
|
||||
@JsonProperty("realm")
|
||||
protected String realm;
|
||||
@@ -17,15 +17,15 @@ public class BaseRealmConfig {
|
||||
protected String realmKey;
|
||||
@JsonProperty("auth-server-url")
|
||||
protected String authServerUrl;
|
||||
@JsonProperty("ssl-not-required")
|
||||
protected boolean sslNotRequired;
|
||||
@JsonProperty("ssl-required")
|
||||
protected String sslRequired;
|
||||
|
||||
public boolean isSslNotRequired() {
|
||||
return sslNotRequired;
|
||||
public String getSslRequired() {
|
||||
return sslRequired;
|
||||
}
|
||||
|
||||
public void setSslNotRequired(boolean sslNotRequired) {
|
||||
this.sslNotRequired = sslNotRequired;
|
||||
public void setSslRequired(String sslRequired) {
|
||||
this.sslRequired = sslRequired;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -22,6 +21,7 @@ public class ApplicationRepresentation {
|
||||
protected Integer notBefore;
|
||||
protected Boolean bearerOnly;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean fullScopeAllowed;
|
||||
|
||||
|
||||
public String getId() {
|
||||
@@ -135,4 +135,12 @@ public class ApplicationRepresentation {
|
||||
public void setPublicClient(Boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
}
|
||||
@@ -95,4 +95,40 @@ public class ClaimRepresentation {
|
||||
public void setPhone(boolean phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ClaimRepresentation that = (ClaimRepresentation) o;
|
||||
|
||||
if (address != that.address) return false;
|
||||
if (email != that.email) return false;
|
||||
if (gender != that.gender) return false;
|
||||
if (locale != that.locale) return false;
|
||||
if (name != that.name) return false;
|
||||
if (phone != that.phone) return false;
|
||||
if (picture != that.picture) return false;
|
||||
if (profile != that.profile) return false;
|
||||
if (username != that.username) return false;
|
||||
if (website != that.website) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (name ? 1 : 0);
|
||||
result = 31 * result + (username ? 1 : 0);
|
||||
result = 31 * result + (profile ? 1 : 0);
|
||||
result = 31 * result + (picture ? 1 : 0);
|
||||
result = 31 * result + (website ? 1 : 0);
|
||||
result = 31 * result + (email ? 1 : 0);
|
||||
result = 31 * result + (gender ? 1 : 0);
|
||||
result = 31 * result + (locale ? 1 : 0);
|
||||
result = 31 * result + (address ? 1 : 0);
|
||||
result = 31 * result + (phone ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,23 @@ package org.keycloak.representations.idm;
|
||||
public class CredentialRepresentation {
|
||||
public static final String SECRET = "secret";
|
||||
public static final String PASSWORD = "password";
|
||||
public static final String PASSWORD_TOKEN = "password-token";
|
||||
public static final String TOTP = "totp";
|
||||
public static final String CLIENT_CERT = "cert";
|
||||
|
||||
protected String type;
|
||||
protected String value;
|
||||
protected String device;
|
||||
|
||||
// Plain-text value of credential (used for example during import from manually created JSON file)
|
||||
protected String value;
|
||||
|
||||
// Value stored in DB (used for example during export/import)
|
||||
protected String hashedSaltedValue;
|
||||
protected String salt;
|
||||
protected Integer hashIterations;
|
||||
// only used when updating a credential. Might set required action
|
||||
protected boolean temporary;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
@@ -37,4 +47,36 @@ public class CredentialRepresentation {
|
||||
public void setDevice(String device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public String getHashedSaltedValue() {
|
||||
return hashedSaltedValue;
|
||||
}
|
||||
|
||||
public void setHashedSaltedValue(String hashedSaltedValue) {
|
||||
this.hashedSaltedValue = hashedSaltedValue;
|
||||
}
|
||||
|
||||
public String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(String salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public Integer getHashIterations() {
|
||||
return hashIterations;
|
||||
}
|
||||
|
||||
public void setHashIterations(Integer hashIterations) {
|
||||
this.hashIterations = hashIterations;
|
||||
}
|
||||
|
||||
public boolean isTemporary() {
|
||||
return temporary;
|
||||
}
|
||||
|
||||
public void setTemporary(boolean temporary) {
|
||||
this.temporary = temporary;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package org.keycloak.representations.idm;
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ErrorRepresentation {
|
||||
public String errorMessage;
|
||||
private String errorMessage;
|
||||
|
||||
public ErrorRepresentation() {
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.List;
|
||||
public class OAuthClientRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String baseUrl;
|
||||
protected List<String> redirectUris;
|
||||
protected List<String> webOrigins;
|
||||
protected Boolean enabled;
|
||||
@@ -17,6 +16,8 @@ public class OAuthClientRepresentation {
|
||||
protected ClaimRepresentation claims;
|
||||
protected Integer notBefore;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean directGrantsOnly;
|
||||
protected Boolean fullScopeAllowed;
|
||||
|
||||
|
||||
public String getId() {
|
||||
@@ -43,14 +44,6 @@ public class OAuthClientRepresentation {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public List<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
@@ -98,4 +91,21 @@ public class OAuthClientRepresentation {
|
||||
public void setPublicClient(Boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
||||
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
|
||||
this.directGrantsOnly = directGrantsOnly;
|
||||
}
|
||||
|
||||
public Boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmEventsConfigRepresentation {
|
||||
protected boolean eventsEnabled;
|
||||
protected Long eventsExpiration;
|
||||
protected List<String> eventsListeners;
|
||||
|
||||
public boolean isEventsEnabled() {
|
||||
return eventsEnabled;
|
||||
}
|
||||
|
||||
public void setEventsEnabled(boolean eventsEnabled) {
|
||||
this.eventsEnabled = eventsEnabled;
|
||||
}
|
||||
|
||||
public Long getEventsExpiration() {
|
||||
return eventsExpiration;
|
||||
}
|
||||
|
||||
public void setEventsExpiration(Long eventsExpiration) {
|
||||
this.eventsExpiration = eventsExpiration;
|
||||
}
|
||||
|
||||
public List<String> getEventsListeners() {
|
||||
return eventsListeners;
|
||||
}
|
||||
|
||||
public void setEventsListeners(List<String> eventsListeners) {
|
||||
this.eventsListeners = eventsListeners;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1501 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user