Compare commits
800 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c516025070 | |
|
|
71e71f15ff | |
|
|
448b08c43c | |
|
|
06f87f85fc | |
|
|
f2c44b7e74 | |
|
|
94d81c32ba | |
|
|
a96a13c630 | |
|
|
06f3ebc7c2 | |
|
|
0dce0aa69e | |
|
|
8a665e1cb1 | |
|
|
dce1a94d9a | |
|
|
76fb6a4f9c | |
|
|
8d38080354 | |
|
|
be803939d8 | |
|
|
af1bbb2f32 | |
|
|
c5a5a9961b | |
|
|
6e2fbec2a4 | |
|
|
638c2d6fe4 | |
|
|
efd6dbb491 | |
|
|
905ce61e82 | |
|
|
3ba892e482 | |
|
|
62cc93399f | |
|
|
cd67d874a2 | |
|
|
46381476e9 | |
|
|
d2538f378d | |
|
|
f14d836e03 | |
|
|
e2e4ea9fa1 | |
|
|
df89820368 | |
|
|
3005cc8607 | |
|
|
e0374d61ac | |
|
|
360ff59ddd | |
|
|
59f42b7edf | |
|
|
409bd553ba | |
|
|
3ec16e8aaf | |
|
|
08bedcf889 | |
|
|
da7ff03cff | |
|
|
5aaf09b3e4 | |
|
|
fd176bc1a7 | |
|
|
9f09c15673 | |
|
|
e4c1df0280 | |
|
|
a00064d1d8 | |
|
|
21d5f37322 | |
|
|
872fc49fb7 | |
|
|
18c79fcf00 | |
|
|
9394a6e190 | |
|
|
3e99e678ca | |
|
|
2beda557e1 | |
|
|
a233009f68 | |
|
|
e7d2c87668 | |
|
|
8a972d38e1 | |
|
|
2ac1e1b7a6 | |
|
|
bca6d9b830 | |
|
|
9e89fe960a | |
|
|
15567ef2a6 | |
|
|
5b47efa407 | |
|
|
3ff317846f | |
|
|
e39d35fbd7 | |
|
|
0fbe33a7e5 | |
|
|
b531896715 | |
|
|
e3b109264c | |
|
|
81cdceca8f | |
|
|
5b5ca68d05 | |
|
|
2728f0597d | |
|
|
5bd4465298 | |
|
|
7d9ee60b73 | |
|
|
5f82143b00 | |
|
|
1fad31d8b0 | |
|
|
3d391b62b8 | |
|
|
913c412fba | |
|
|
b355b526d6 | |
|
|
858b0ca77d | |
|
|
c00ee02f44 | |
|
|
40848d251e | |
|
|
a7b7660772 | |
|
|
440f2edaa6 | |
|
|
8667f0b946 | |
|
|
60a8cb3825 | |
|
|
11a53a6fc2 | |
|
|
58a54c7372 | |
|
|
53f34e175e | |
|
|
9279ab8d54 | |
|
|
6352b7991a | |
|
|
db9f0a3301 | |
|
|
4c4202de24 | |
|
|
6f7ef142bc | |
|
|
7e34235578 | |
|
|
07a700f531 | |
|
|
31756bd6fc | |
|
|
95583500c0 | |
|
|
c096b4c82b | |
|
|
63d638b452 | |
|
|
ee21f2874a | |
|
|
24c3f92030 | |
|
|
3b312e1cff | |
|
|
7f0b42df31 | |
|
|
4d84e02c5f | |
|
|
ca5e5f1f06 | |
|
|
387e7249e0 | |
|
|
8a7e04d7cf | |
|
|
11d3c21889 | |
|
|
a51fd8f8f8 | |
|
|
1a6da20b3e | |
|
|
2a3adb4ec4 | |
|
|
a5a7d30791 | |
|
|
bab29c84f4 | |
|
|
8eaec09704 | |
|
|
da07e4815b | |
|
|
de1f05dc8b | |
|
|
1300774928 | |
|
|
f946c2845b | |
|
|
3cd4e12fa9 | |
|
|
3e5eeaabe1 | |
|
|
fdc948b8ec | |
|
|
1966a26d6f | |
|
|
c2886ea70c | |
|
|
850a6c0373 | |
|
|
764d2220d6 | |
|
|
77abda055b | |
|
|
505160b15e | |
|
|
fdab7aea5b | |
|
|
9e95c8a10c | |
|
|
f7234f9aea | |
|
|
0e4c82cc94 | |
|
|
8596db4de3 | |
|
|
ccf8b89dee | |
|
|
3f45530e0e | |
|
|
46341d3e42 | |
|
|
9fabd9fbf1 | |
|
|
be29e420c0 | |
|
|
2734d45f3e | |
|
|
601959f85c | |
|
|
bf4e1e60b6 | |
|
|
0c041d5376 | |
|
|
a7101b6cc8 | |
|
|
7ce4cb8aa5 | |
|
|
a95a98dd9b | |
|
|
a3dbe594c3 | |
|
|
49f9a4e309 | |
|
|
fd72cb6bfc | |
|
|
78b2997c5d | |
|
|
cfebf0acc7 | |
|
|
874958641a | |
|
|
691bf12697 | |
|
|
4332186ea7 | |
|
|
52274f828f | |
|
|
5c3d185e8d | |
|
|
35d611b8e8 | |
|
|
d940cfd417 | |
|
|
1d78f74b2a | |
|
|
12da0340a1 | |
|
|
e283a9a7a5 | |
|
|
1c29fa7ebd | |
|
|
7bea9bb258 | |
|
|
4ada1b1327 | |
|
|
32d14893ae | |
|
|
0f395b3a2c | |
|
|
0e750de5f5 | |
|
|
26479f3d99 | |
|
|
11e9b543e5 | |
|
|
5c2c6b00aa | |
|
|
f0bd481a13 | |
|
|
8fb8e9d0e4 | |
|
|
3213920bff | |
|
|
01812e755d | |
|
|
d35172dff9 | |
|
|
1999bd4670 | |
|
|
4f411c4342 | |
|
|
c6a46da7fa | |
|
|
c8c0d844b4 | |
|
|
cf7e09b1c4 | |
|
|
68a0cc0433 | |
|
|
c8eb5b4e5a | |
|
|
0f69d262e8 | |
|
|
748a7172f5 | |
|
|
d1c3660cda | |
|
|
78470351f6 | |
|
|
9e7ad3f046 | |
|
|
3688949646 | |
|
|
a091c2795b | |
|
|
411a105fb1 | |
|
|
bd7ebc11c7 | |
|
|
61c842124e | |
|
|
bb95fd733f | |
|
|
2b61ca1df3 | |
|
|
b61487766c | |
|
|
1f6881829b | |
|
|
a8ec3de36e | |
|
|
a4cde4359e | |
|
|
44adee82ec | |
|
|
97632483aa | |
|
|
2362481941 | |
|
|
733ce5ba5d | |
|
|
66742817cc | |
|
|
0e333ec040 | |
|
|
a09641b184 | |
|
|
28ffa12548 | |
|
|
f6f20da863 | |
|
|
22e77d2d12 | |
|
|
93dac9b3fe | |
|
|
db0b8f8a42 | |
|
|
1c5dee3edf | |
|
|
abacb75df2 | |
|
|
96f51465a8 | |
|
|
58de00e34b | |
|
|
8cc5ad182c | |
|
|
e3f532acde | |
|
|
86b93cbb26 | |
|
|
cc756cb72d | |
|
|
e2798c09cb | |
|
|
f06895bc18 | |
|
|
18d981efaa | |
|
|
080405b4c0 | |
|
|
45d4180a7f | |
|
|
77c013b03b | |
|
|
7513f94877 | |
|
|
2b1b7a142a | |
|
|
ba50664582 | |
|
|
bafe64e7cd | |
|
|
b2e1643893 | |
|
|
e155261a6b | |
|
|
c39735729d | |
|
|
2a570b26f1 | |
|
|
1d4312b64f | |
|
|
af78b8703f | |
|
|
0e943179f1 | |
|
|
72e9aa9378 | |
|
|
c5d1b1ef84 | |
|
|
40dcc8b34a | |
|
|
ba5982e1eb | |
|
|
b6f6b5de76 | |
|
|
93561a297f | |
|
|
c979e64006 | |
|
|
306ec75569 | |
|
|
a061764ca1 | |
|
|
7fbac753ee | |
|
|
15100f3c39 | |
|
|
87ac085dfd | |
|
|
a34a397a68 | |
|
|
94b23a7f2d | |
|
|
c015e226ba | |
|
|
5d18b34159 | |
|
|
ae50749c5f | |
|
|
3875c39a21 | |
|
|
b208e94b93 | |
|
|
c00da6bb81 | |
|
|
486aa66587 | |
|
|
527f60f334 | |
|
|
c06facd9e1 | |
|
|
962c8185ae | |
|
|
6280319d80 | |
|
|
2d6b80842b | |
|
|
b8f2e0a32c | |
|
|
47efae9e92 | |
|
|
ef2baa8983 | |
|
|
9d57eb3655 | |
|
|
f341f50cd5 | |
|
|
52edf95202 | |
|
|
f9202b9a8a | |
|
|
b02a122e9d | |
|
|
ccf8e718b2 | |
|
|
91dd394a08 | |
|
|
6468166c40 | |
|
|
6f84cc2356 | |
|
|
f5a0ee6987 | |
|
|
e56e0d2aa0 | |
|
|
dfb13e5397 | |
|
|
552b9f9fac | |
|
|
1e1a0ec5e8 | |
|
|
24f1f9c814 | |
|
|
059ec7d527 | |
|
|
f2ea4129bd | |
|
|
88789ef358 | |
|
|
5152f40be9 | |
|
|
1fc896c458 | |
|
|
1561da75ad | |
|
|
834531c7a6 | |
|
|
2f7729a892 | |
|
|
f998bb07c3 | |
|
|
47fb9c0fbb | |
|
|
4d1d62bd62 | |
|
|
bad68332a6 | |
|
|
2ae54e8d98 | |
|
|
6be07281d5 | |
|
|
7db200e8c6 | |
|
|
527f0c6d28 | |
|
|
7d5a9bc124 | |
|
|
92371f794a | |
|
|
9e95ee5825 | |
|
|
86a8f7395b | |
|
|
9d4cafd2c9 | |
|
|
745fda6113 | |
|
|
aa6cab7d8d | |
|
|
9b71825e1a | |
|
|
2e27e09f3b | |
|
|
b4d9570bdc | |
|
|
7cb5380e75 | |
|
|
e7292c78a0 | |
|
|
771288475f | |
|
|
ee4a05d409 | |
|
|
f1cd2441ca | |
|
|
95c5d8ee73 | |
|
|
f1908ba5fc | |
|
|
8e1a7789b1 | |
|
|
b20d4d241c | |
|
|
bc586d7b4b | |
|
|
06b566e2d6 | |
|
|
3f0a1c6883 | |
|
|
812198b6a5 | |
|
|
88d847102c | |
|
|
40291a4478 | |
|
|
184868b296 | |
|
|
5a68c92268 | |
|
|
f09ee1c0e7 | |
|
|
971d4b4854 | |
|
|
f04dedd9bb | |
|
|
53dbb48882 | |
|
|
61bd0eeb86 | |
|
|
e4b767700a | |
|
|
346374ebc8 | |
|
|
4b63e06418 | |
|
|
32963967d4 | |
|
|
9d9e5cad72 | |
|
|
598fb02dda | |
|
|
a417f78521 | |
|
|
a5e2b8ae61 | |
|
|
650cee05bc | |
|
|
e5f933754b | |
|
|
1bdc456bf4 | |
|
|
8067714256 | |
|
|
85b0454e8d | |
|
|
2eb132c0b4 | |
|
|
0022c622e1 | |
|
|
ab105cdcfa | |
|
|
1f3a745ec8 | |
|
|
ba47157062 | |
|
|
4f746b3b58 | |
|
|
d7538dfd61 | |
|
|
335d3f3a3e | |
|
|
618a99a245 | |
|
|
0d2740ec0c | |
|
|
69bba0c21a | |
|
|
f0047eb8eb | |
|
|
60b8ad70f8 | |
|
|
d46e3c6220 | |
|
|
8ff374b949 | |
|
|
e9b79194d7 | |
|
|
72685767f7 | |
|
|
bb11bfa7c8 | |
|
|
bdecba6431 | |
|
|
67b4b26819 | |
|
|
f3d7156482 | |
|
|
8bab6d1f4e | |
|
|
76ba57a541 | |
|
|
ec744b2c9f | |
|
|
4db9948c19 | |
|
|
0a7ffac846 | |
|
|
c8e50905bb | |
|
|
eaee34b361 | |
|
|
6ce10daea0 | |
|
|
f98e776869 | |
|
|
df07758a89 | |
|
|
302822f361 | |
|
|
e1e12a5f3f | |
|
|
8c77fc4eaf | |
|
|
607d53a2aa | |
|
|
70b8aa8032 | |
|
|
806672e525 | |
|
|
853450a8a6 | |
|
|
05873137dc | |
|
|
8bc89e648a | |
|
|
f1ac21943d | |
|
|
c6c106c375 | |
|
|
0a6ce18150 | |
|
|
73e2a63a5b | |
|
|
77e5340c3c | |
|
|
573976cda9 | |
|
|
dc028a6652 | |
|
|
50fc28034d | |
|
|
3c40922847 | |
|
|
d72913b53a | |
|
|
a31979920b | |
|
|
8ec71aa2c6 | |
|
|
74f0a209dd | |
|
|
f8f5b908d0 | |
|
|
d3ba9c0ae7 | |
|
|
f68281d0e4 | |
|
|
863038605f | |
|
|
f5b2275913 | |
|
|
1edef62ecb | |
|
|
7c58629958 | |
|
|
70ed5fd8bb | |
|
|
6e261e7251 | |
|
|
0652f4ee23 | |
|
|
d8980535b7 | |
|
|
dd0818b84f | |
|
|
4527671568 | |
|
|
e998985732 | |
|
|
6f7fe61780 | |
|
|
db3610123f | |
|
|
380585d391 | |
|
|
4aef368057 | |
|
|
6de80009cf | |
|
|
bec0e6bb2e | |
|
|
2a98556daa | |
|
|
f49c08ce93 | |
|
|
ec92d97518 | |
|
|
34458db530 | |
|
|
0b220c6f55 | |
|
|
39f0f29dd9 | |
|
|
d1da02f457 | |
|
|
d7ca6a098e | |
|
|
f17c4d6087 | |
|
|
a780e39ab9 | |
|
|
bef2a88603 | |
|
|
3af5683dc1 | |
|
|
5109132df3 | |
|
|
51a9192792 | |
|
|
c7e66d7df2 | |
|
|
d011f5e64a | |
|
|
17f887595f | |
|
|
324b0919b6 | |
|
|
461cdcd8fc | |
|
|
4e3f761efe | |
|
|
0174cfbbb5 | |
|
|
f57f0ff096 | |
|
|
117f32006f | |
|
|
db53818de7 | |
|
|
ecef8dd722 | |
|
|
b34c4e104f | |
|
|
a0f8845ecf | |
|
|
b18dbfe0a9 | |
|
|
42ce676ef0 | |
|
|
aa3e07bd79 | |
|
|
112f068607 | |
|
|
d191ac8ee6 | |
|
|
61a92c0e7f | |
|
|
266eba7c2d | |
|
|
9de2be91a2 | |
|
|
566c8690c8 | |
|
|
aeaa76ce46 | |
|
|
a44b4c1229 | |
|
|
a4e984e4ea | |
|
|
2c9090155a | |
|
|
b4fbf23361 | |
|
|
c61dea96bf | |
|
|
99763ab2b6 | |
|
|
7ac4c7afb7 | |
|
|
4e7bf66d68 | |
|
|
c4c3223140 | |
|
|
4a0e960c32 | |
|
|
7069a4f6fb | |
|
|
6aa2379268 | |
|
|
80f7f3647e | |
|
|
3373d7f767 | |
|
|
896958337a | |
|
|
72766daac5 | |
|
|
9714053f07 | |
|
|
286105266f | |
|
|
f018f54c4c | |
|
|
ed4802f943 | |
|
|
9b33321cc2 | |
|
|
47f825871e | |
|
|
d2b93a237f | |
|
|
0a8fd79a44 | |
|
|
00ee9e378d | |
|
|
248643cf30 | |
|
|
dfb3629dc2 | |
|
|
511bce2471 | |
|
|
3773fc9e54 | |
|
|
b97d03d126 | |
|
|
a4df6582ee | |
|
|
75584a2c33 | |
|
|
0ef575ae84 | |
|
|
71b366cbf3 | |
|
|
b9ac92cf25 | |
|
|
887da3bffd | |
|
|
90ccc41db3 | |
|
|
80ff9857f0 | |
|
|
44d1413fa7 | |
|
|
c857e4610b | |
|
|
590a79c6a6 | |
|
|
36b8db1e86 | |
|
|
4d7b2517a9 | |
|
|
4d12772bc4 | |
|
|
8262bc1fff | |
|
|
a199892c73 | |
|
|
00a507d51b | |
|
|
19e5e0671b | |
|
|
2a2fc56031 | |
|
|
89e6bfd521 | |
|
|
6ba3f6f884 | |
|
|
d0d7fd684d | |
|
|
57d2d66a1d | |
|
|
fe9dac934a | |
|
|
577dafef0e | |
|
|
cef064f34b | |
|
|
b3afc92fc7 | |
|
|
5b1bd87e7a | |
|
|
82c95aaf19 | |
|
|
328692bb40 | |
|
|
46416fe741 | |
|
|
6b50b18d66 | |
|
|
575de47020 | |
|
|
259afc7a62 | |
|
|
b697a4b0ba | |
|
|
5ce3bcb3d3 | |
|
|
75406c916e | |
|
|
517f94bd97 | |
|
|
526c65fb39 | |
|
|
adb668d9ed | |
|
|
15103333e5 | |
|
|
8ab75b883a | |
|
|
4b87b4a9f5 | |
|
|
715659ebcf | |
|
|
a9d640b1b0 | |
|
|
b802354fdb | |
|
|
fddafb753f | |
|
|
1993718cc7 | |
|
|
180f1f803b | |
|
|
9674ad8a45 | |
|
|
8a7e32e5be | |
|
|
d413ee5cf8 | |
|
|
077e991ae6 | |
|
|
8643e85b1e | |
|
|
73e36bf6d2 | |
|
|
8df9f0272b | |
|
|
091f1d5f90 | |
|
|
4802ad8c17 | |
|
|
2b53bdf882 | |
|
|
c36c8e952c | |
|
|
e6e0f907b8 | |
|
|
089fca4ca4 | |
|
|
4f79cd1014 | |
|
|
2e6df8c5b7 | |
|
|
3964e739c0 | |
|
|
c34ff785dd | |
|
|
ad5525d2e5 | |
|
|
6e12f43b16 | |
|
|
3063afc756 | |
|
|
974c3beb67 | |
|
|
d102c50779 | |
|
|
d1ae0f5bbf | |
|
|
b54cd179e2 | |
|
|
cd4dcc9fc9 | |
|
|
b4367f595e | |
|
|
2f9d384b1e | |
|
|
06ccec017d | |
|
|
105bd17b61 | |
|
|
f2df7a8603 | |
|
|
d69e083f28 | |
|
|
7acd9adc4b | |
|
|
c3ad115f5f | |
|
|
eed831191a | |
|
|
67bb0a6ef8 | |
|
|
e95d867792 | |
|
|
5af7c331b7 | |
|
|
27d89ccb30 | |
|
|
f733760e68 | |
|
|
9294fbdffd | |
|
|
c26329d21b | |
|
|
d347f65bc6 | |
|
|
82c0207c25 | |
|
|
980d0ff116 | |
|
|
ac8a134192 | |
|
|
5ad57084cc | |
|
|
41e7258af3 | |
|
|
dcd9bda3df | |
|
|
fc1eaa1665 | |
|
|
5f1a30a314 | |
|
|
010939de74 | |
|
|
68eb4aef23 | |
|
|
a754b40b30 | |
|
|
a708d22469 | |
|
|
caa2fccd32 | |
|
|
bc6229b00e | |
|
|
008debf267 | |
|
|
afa83081a6 | |
|
|
3deac9e1a9 | |
|
|
5f1e5e0ed4 | |
|
|
bacf0c6142 | |
|
|
ee2b719098 | |
|
|
ec0c7d5162 | |
|
|
b2f038a174 | |
|
|
bae8e68f55 | |
|
|
2b9cec59be | |
|
|
3816e29861 | |
|
|
eadf300985 | |
|
|
6e3f802cba | |
|
|
616bbfa403 | |
|
|
e10299896a | |
|
|
37b4058640 | |
|
|
09ffc2c05d | |
|
|
d0ecca4274 | |
|
|
eff195aaa9 | |
|
|
7302ca6a99 | |
|
|
81e81256e0 | |
|
|
8673f2c294 | |
|
|
5f3700670c | |
|
|
1775ae99ec | |
|
|
0f9909406f | |
|
|
992a081434 | |
|
|
88c69a7caa | |
|
|
4b454e1e2e | |
|
|
78c6ac29f9 | |
|
|
26e959c3d0 | |
|
|
7345e03a6f | |
|
|
c4d63f74ca | |
|
|
bbcd532924 | |
|
|
de2ab6a3ad | |
|
|
a19c27f75b | |
|
|
80693a3db9 | |
|
|
477fc582f0 | |
|
|
84aa06629f | |
|
|
5cd43c2139 | |
|
|
b1f623128c | |
|
|
e56601db95 | |
|
|
0d8c085d19 | |
|
|
91046c779c | |
|
|
d754bc8d8c | |
|
|
fb42fefaf9 | |
|
|
97e6573e32 | |
|
|
e055585cf2 | |
|
|
3b6ccaac0a | |
|
|
29165243a5 | |
|
|
6a98fc61a5 | |
|
|
df6131ba7d | |
|
|
3d965426ac | |
|
|
e0af12eeba | |
|
|
413837e4df | |
|
|
f3b6ae5a4d | |
|
|
49f556575e | |
|
|
d5bc8e3757 | |
|
|
37ed0719e3 | |
|
|
7d4c35417f | |
|
|
8941d96a99 | |
|
|
e9569564d7 | |
|
|
3b1c1ac85f | |
|
|
35a6b08dac | |
|
|
84ad620b34 | |
|
|
8c53e4b0e6 | |
|
|
90d6625e7f | |
|
|
ec87c31ecd | |
|
|
035a6f7a8e | |
|
|
f6ff486939 | |
|
|
18b70ee8ce | |
|
|
2055d0891b | |
|
|
cbb51b0506 | |
|
|
4a36b023a6 | |
|
|
befd53e456 | |
|
|
1abdee8399 | |
|
|
c6b1bc775a | |
|
|
82d03364c8 | |
|
|
5faf72b876 | |
|
|
aa4afd9c5f | |
|
|
1911c7242c | |
|
|
f68afc177c | |
|
|
c459d1adde | |
|
|
0b59f0c7fd | |
|
|
b0044cd075 | |
|
|
df8211cf68 | |
|
|
0270596bac | |
|
|
75b51c85bd | |
|
|
54964cb5a4 | |
|
|
ff5a99ea0a | |
|
|
ac08968c30 | |
|
|
a947490c04 | |
|
|
5857589a44 | |
|
|
c7c8404259 | |
|
|
29866f923e | |
|
|
4bd7b522cb | |
|
|
b99b45d1ba | |
|
|
a1b9031416 | |
|
|
ce46854baa | |
|
|
704f1b8a67 | |
|
|
f64a23b1ae | |
|
|
f023fa5282 | |
|
|
54afd070ec | |
|
|
0f3d8a5c60 | |
|
|
9b8ce98ebf | |
|
|
cd3ecb8e3c | |
|
|
9ffca231a0 | |
|
|
74a8cff651 | |
|
|
2449a7a61b | |
|
|
649b39b1c8 | |
|
|
b1f6e24f7e | |
|
|
40c006389e | |
|
|
07cc6b62f3 | |
|
|
c312ca0b4f | |
|
|
4776006b07 | |
|
|
6a942e294b | |
|
|
8bd5e8ba07 | |
|
|
4e5b27bdd1 | |
|
|
56822871d6 | |
|
|
206c5180a7 | |
|
|
07ea7b92da | |
|
|
0e289c70b5 | |
|
|
dcddfe5d65 | |
|
|
587c9c41a3 | |
|
|
a57e3d401f | |
|
|
01fd618436 | |
|
|
23711eb6a1 | |
|
|
b9208f9077 | |
|
|
975bf7700c | |
|
|
b76ce0dcc9 | |
|
|
f5984a088d | |
|
|
92c2b081cd | |
|
|
d6f11f89a2 | |
|
|
4ef10cd168 | |
|
|
96a31f5afd | |
|
|
856d2b07f9 | |
|
|
ef9caa321e | |
|
|
3f2a1c4c69 | |
|
|
6459137bb2 | |
|
|
ff84c43b45 | |
|
|
7f56406ca6 | |
|
|
f5d698c5de | |
|
|
c652557f8a | |
|
|
30c2d9e3c9 | |
|
|
111190063d | |
|
|
79e706e59f | |
|
|
b16412731d | |
|
|
bdb354531e | |
|
|
ccdd0596c7 | |
|
|
92ebc16288 | |
|
|
1a86ba2633 | |
|
|
5435445f11 | |
|
|
95f63d7743 | |
|
|
42cc968a2e | |
|
|
8a32fd2061 | |
|
|
f0a78e0edf | |
|
|
f9e3ae8abb | |
|
|
590d87d265 | |
|
|
a14767c232 | |
|
|
745c45718c | |
|
|
9b1ba497e3 | |
|
|
bddd8f0622 | |
|
|
cbbab079a7 | |
|
|
ad62b2fc2a | |
|
|
e78706747a | |
|
|
1031f01ee0 | |
|
|
de82cb0a85 | |
|
|
7dd013cb9f | |
|
|
b38a27626a | |
|
|
866e2b99ba | |
|
|
5761e0cb61 | |
|
|
7d4c321b3c | |
|
|
c71748d104 | |
|
|
7191670c4e | |
|
|
d0be75e7b3 | |
|
|
d00986afc7 | |
|
|
d419c68670 | |
|
|
fb1e4e5a84 | |
|
|
d237b339c6 | |
|
|
a1f983ae6b | |
|
|
720c840eec | |
|
|
af2530d57d | |
|
|
e0ca0a6e35 | |
|
|
eee667a611 | |
|
|
3d09247029 | |
|
|
415d118b5c | |
|
|
e8f438db55 | |
|
|
e3d38a70a9 | |
|
|
e1f533c759 | |
|
|
2e0e6c85c0 | |
|
|
fb98e1285f | |
|
|
98d4d53418 | |
|
|
2863bd39e5 | |
|
|
9673f33f53 | |
|
|
4252731534 | |
|
|
ff11894493 | |
|
|
44d180c864 | |
|
|
88b87074dc | |
|
|
88b89a3756 | |
|
|
ab21368d94 | |
|
|
78054e3dbd | |
|
|
2445b49e0a | |
|
|
56d0d2e023 | |
|
|
fe8e8d8bb3 | |
|
|
a7d06fd666 | |
|
|
74b3d60843 | |
|
|
c12836c9d6 | |
|
|
ef520c834f | |
|
|
f92005dd8e | |
|
|
1f770cd549 | |
|
|
13810e2b70 | |
|
|
bab75f9092 | |
|
|
5bf8ccfa4a | |
|
|
44cf029138 | |
|
|
a160d37c05 | |
|
|
fa78a2f626 | |
|
|
dbd1017a8a | |
|
|
9f9686ee61 | |
|
|
22fc6d344b | |
|
|
ea549d1a9b | |
|
|
93beba2242 | |
|
|
a9f0ed72e6 | |
|
|
ccbf08d8aa | |
|
|
d8f5359a52 | |
|
|
ee788ac57e | |
|
|
85bbf086ba |
|
|
@ -0,0 +1,20 @@
|
|||
#
|
||||
# Licensed to Apereo under one or more contributor license
|
||||
# agreements. See the NOTICE file distributed with this work
|
||||
# for additional information regarding copyright ownership.
|
||||
# Apereo licenses this file to you under the Apache License,
|
||||
# Version 2.0 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a
|
||||
# copy of the License at the following location:
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
custom: ['https://www.apereo.org/content/apereo-membership']
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":preserveSemverRanges",
|
||||
":rebaseStalePrs",
|
||||
":disableRateLimiting",
|
||||
":semanticCommits",
|
||||
":semanticCommitTypeAll(renovatebot)"
|
||||
],
|
||||
"labels": ["dependencies", "bot"]
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#
|
||||
# Licensed to Apereo under one or more contributor license
|
||||
# agreements. See the NOTICE file distributed with this work
|
||||
# for additional information regarding copyright ownership.
|
||||
# Apereo licenses this file to you under the Apache License,
|
||||
# Version 2.0 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a
|
||||
# copy of the License at the following location:
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 7
|
||||
|
||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: Pending
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This patch has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
closeComment: >
|
||||
This patch has been automatically closed because it has not had
|
||||
recent activity. If you wish to resume work, please re-open the pull request
|
||||
and continue as usual. Thank you for your contributions.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: pulls
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
.classpath
|
||||
!/.project
|
||||
.project
|
||||
.settings
|
||||
bin
|
||||
target
|
||||
*.ipr
|
||||
*.iml
|
||||
*.iws
|
||||
.idea/
|
||||
.DS_Store
|
||||
.idea
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# Licensed to Apereo under one or more contributor license
|
||||
# agreements. See the NOTICE file distributed with this work
|
||||
# for additional information regarding copyright ownership.
|
||||
# Apereo licenses this file to you under the Apache License,
|
||||
# Version 2.0 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a
|
||||
# copy of the License at the following location:
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
pull_request_rules:
|
||||
- name: automatic merge by dependabot
|
||||
conditions:
|
||||
- status-success=continuous-integration/travis-ci/pr
|
||||
- status-success=WIP
|
||||
- "#changes-requested-reviews-by=0"
|
||||
- base=master
|
||||
- label=dependencies
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
strict: false
|
||||
delete_head_branch:
|
||||
- name: automatic merge by renovate
|
||||
conditions:
|
||||
- status-success=continuous-integration/travis-ci/pr
|
||||
- status-success=WIP
|
||||
- "#changes-requested-reviews-by=0"
|
||||
- base=master
|
||||
- label=dependencies
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
strict: false
|
||||
delete_head_branch:
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# Licensed to Jasig under one or more contributor license
|
||||
# agreements. See the NOTICE file distributed with this work
|
||||
# for additional information regarding copyright ownership.
|
||||
# Jasig licenses this file to you under the Apache License,
|
||||
# Version 2.0 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a
|
||||
# copy of the License at the following location:
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
language: java
|
||||
sudo: required
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.m2/repository"
|
||||
script: "mvn install --settings travis/settings.xml"
|
||||
jdk:
|
||||
- openjdk8
|
||||
env:
|
||||
global:
|
||||
- secure: "JM/FMiec3GYShrMlJQSW2QG208+V0GCAj2bsP5eF8q4yzgp6o4rT+r57KDIDD6MapRN+G1Pnl3WPcS0aQYnwOhPg4tA2De1bFUPaJltP47eHFfblpjZeHMxcauCQ6BwFFr8yuC0ORsYCW3TOK00Mxq4CRlTlg5iclzHyS/pnkLI="
|
||||
- secure: "eXfgf3v8Kw/L22DO39Y61os13bfNpop8Xpkmz+HZ1djQWavOkRn58gSg8EVjBYRPOrTuEbhEWb+s3qpx8j3qRdi6roMs9MTr5gEPTAyjTtJ/Zv1qhJ6OlEl2w5c2fRMsk5cB//mtxtZ+qMaz6sdZI2csbQ2xlhjz4AbGQL5i1lY="
|
||||
|
||||
after_success:
|
||||
- chmod -R 777 ./travis/deploy-to-sonatype.sh
|
||||
- ./travis/deploy-to-sonatype.sh
|
||||
|
|
@ -1,31 +1,50 @@
|
|||
License for Use
|
||||
|
||||
Copyright (c) 2007, JA-SIG, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the JA-SIG, Inc. nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
====
|
||||
Licensed to Jasig under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Jasig licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on
|
||||
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
====
|
||||
|
||||
License for Use
|
||||
|
||||
Copyright (c) 2007, JA-SIG, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the JA-SIG, Inc. nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
This project includes:
|
||||
Apache Commons Codec under Apache License, Version 2.0
|
||||
Apache Log4j under The Apache Software License, Version 2.0
|
||||
Apache Log4j API under Apache License, Version 2.0
|
||||
Apache Log4j to SLF4J Adapter under Apache License, Version 2.0
|
||||
Apache XML Security under The Apache Software License, Version 2.0
|
||||
Apereo CAS Client for Java under Apache License Version 2.0
|
||||
asm under BSD
|
||||
asm-analysis under BSD
|
||||
asm-commons under BSD
|
||||
asm-tree under BSD
|
||||
Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence
|
||||
Bouncy Castle Provider under Bouncy Castle Licence
|
||||
catalina under Apache License, Version 2.0
|
||||
coyote under Apache License, Version 2.0
|
||||
Eclipse Compiler for Java(TM) under Eclipse Public License - v 2.0
|
||||
Ehcache Core under The Apache Software License, Version 2.0
|
||||
Hamcrest Core under New BSD License
|
||||
Jackson-annotations under The Apache Software License, Version 2.0
|
||||
Jackson-core under The Apache Software License, Version 2.0
|
||||
jackson-databind under The Apache Software License, Version 2.0
|
||||
Jasig CAS Client for Java - Common Tomcat Integration Support under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Core under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Distributed Proxy Storage Support: Memcached under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - JBoss Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Jetty Container Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - SAML Protocol Support under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Spring Boot Support under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Tomcat 6.x Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Tomcat 7.x Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Tomcat 8.5.x Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Tomcat 8.x Integration under Apache License Version 2.0
|
||||
Jasig CAS Client for Java - Tomcat 9.0.x Integration under Apache License Version 2.0
|
||||
Java Servlet API under CDDL + GPLv2 with classpath exception
|
||||
javax.annotation API under CDDL + GPLv2 with classpath exception
|
||||
JBoss Application Server Tomcat under lgpl
|
||||
JCL 1.2 implemented over SLF4J under MIT License
|
||||
Jetty :: Apache JSP Implementation under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Http Utility under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: IO Utility under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: JNDI Naming under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Plus under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Schemas under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Security under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Server Core under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Servlet Annotations under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Servlet Handling under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Utilities under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: Webapp Application Support under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Jetty :: XML utilities under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
|
||||
Joda-Time under Apache License, Version 2.0
|
||||
JUL to SLF4J bridge under MIT License
|
||||
JUnit under Eclipse Public License 1.0
|
||||
Logback Classic Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License
|
||||
Logback Core Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License
|
||||
MortBay :: Apache EL :: API and Implementation under Apache License Version 2.0
|
||||
MortBay :: Apache Jasper :: JSP Implementation under Apache License Version 2.0
|
||||
SLF4J API Module under MIT License
|
||||
SLF4J Simple Binding under MIT License
|
||||
SnakeYAML under Apache License, Version 2.0
|
||||
Spring AOP under Apache License, Version 2.0
|
||||
Spring Beans under Apache License, Version 2.0
|
||||
Spring Boot under Apache License, Version 2.0
|
||||
Spring Boot AutoConfigure under Apache License, Version 2.0
|
||||
Spring Boot Logging Starter under Apache License, Version 2.0
|
||||
Spring Boot Starter under Apache License, Version 2.0
|
||||
Spring Commons Logging Bridge under Apache License, Version 2.0
|
||||
Spring Context under Apache License, Version 2.0
|
||||
Spring Core under Apache License, Version 2.0
|
||||
Spring Expression Language (SpEL) under Apache License, Version 2.0
|
||||
Spring TestContext Framework under Apache License, Version 2.0
|
||||
Spring Web under Apache License, Version 2.0
|
||||
Spymemcached under The Apache Software License, Version 2.0
|
||||
tomcat-annotations-api under Apache License, Version 2.0
|
||||
tomcat-catalina under Apache License, Version 2.0
|
||||
tomcat-coyote under Apache License, Version 2.0
|
||||
tomcat-el-api under Apache License, Version 2.0
|
||||
tomcat-embed-core under Apache License, Version 2.0
|
||||
tomcat-jaspic-api under Apache License, Version 2.0
|
||||
tomcat-jni under Apache License, Version 2.0
|
||||
tomcat-jsp-api under Apache License, Version 2.0
|
||||
tomcat-util-scan under Apache License, Version 2.0
|
||||
|
||||
138
assembly.xml
138
assembly.xml
|
|
@ -1,64 +1,84 @@
|
|||
<!--
|
||||
|
||||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<assembly>
|
||||
<id>release</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<useStrictFiltering>false</useStrictFiltering>
|
||||
<directory>${basedir}</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>*.xml</include>
|
||||
<include>*.txt</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
<includes></includes>
|
||||
<sources>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src</directory>
|
||||
<outputDirectory>src</outputDirectory>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</fileSet>
|
||||
<id>release</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<useStrictFiltering>false</useStrictFiltering>
|
||||
<directory>${basedir}</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>*.xml</include>
|
||||
<include>*.txt</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
<includes></includes>
|
||||
<sources>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src</directory>
|
||||
<outputDirectory>src</outputDirectory>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</fileSet>
|
||||
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes>
|
||||
<include>*.xml</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes>
|
||||
<include>*.xml</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<directory>target/site/apidocs/</directory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<outputDirectory>docs</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<fileSet>
|
||||
<lineEnding>unix</lineEnding>
|
||||
<directory>target/site/apidocs/</directory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<outputDirectory>docs</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<includeModuleDirectory>true</includeModuleDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</sources>
|
||||
<binaries>
|
||||
<outputDirectory>modules</outputDirectory>
|
||||
<includeDependencies>true</includeDependencies>
|
||||
<unpack>false</unpack>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes />
|
||||
</binaries>
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
<includeModuleDirectory>true</includeModuleDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</sources>
|
||||
<binaries>
|
||||
<outputDirectory>modules</outputDirectory>
|
||||
<includeDependencies>true</includeDependencies>
|
||||
<unpack>false</unpack>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<includes/>
|
||||
</binaries>
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
</assembly>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
====
|
||||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
====
|
||||
|
||||
License for Use
|
||||
|
||||
Copyright (c) 2007, JA-SIG, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the JA-SIG, Inc. nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
This project includes:
|
||||
Apache Commons Codec under Apache License, Version 2.0
|
||||
Apache Log4j under The Apache Software License, Version 2.0
|
||||
Apache XML Security under The Apache Software License, Version 2.0
|
||||
Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence
|
||||
Bouncy Castle Provider under Bouncy Castle Licence
|
||||
Hamcrest Core under New BSD License
|
||||
Jackson-annotations under The Apache Software License, Version 2.0
|
||||
Jackson-core under The Apache Software License, Version 2.0
|
||||
jackson-databind under The Apache Software License, Version 2.0
|
||||
Jasig CAS Client for Java - Core under Apache License Version 2.0
|
||||
Java Servlet API under CDDL + GPLv2 with classpath exception
|
||||
JCL 1.2 implemented over SLF4J under MIT License
|
||||
JUnit under Eclipse Public License 1.0
|
||||
SLF4J API Module under MIT License
|
||||
SLF4J Simple Binding under MIT License
|
||||
Spring AOP under Apache License, Version 2.0
|
||||
Spring Beans under Apache License, Version 2.0
|
||||
Spring Commons Logging Bridge under Apache License, Version 2.0
|
||||
Spring Context under Apache License, Version 2.0
|
||||
Spring Core under Apache License, Version 2.0
|
||||
Spring Expression Language (SpEL) under Apache License, Version 2.0
|
||||
Spring TestContext Framework under Apache License, Version 2.0
|
||||
Spring Web under Apache License, Version 2.0
|
||||
|
||||
|
|
@ -1,53 +1,44 @@
|
|||
<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">
|
||||
<!--
|
||||
|
||||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.1</version>
|
||||
<groupId>org.jasig.cas.client</groupId>
|
||||
<version>3.6.2-SNAPSHOT</version>
|
||||
<artifactId>cas-client</artifactId>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>JA-SIG CAS Client for Java - Core</name>
|
||||
<name>Jasig CAS Client for Java - Core</name>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.4</source>
|
||||
<target>1.4</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Tests*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clover-plugin</artifactId>
|
||||
<configuration>
|
||||
<licenseLocation>${basedir}/src/test/clover/clover.license</licenseLocation>
|
||||
</configuration>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
@ -56,49 +47,70 @@
|
|||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>xml-security</groupId>
|
||||
<artifactId>xmlsec</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xml-security</groupId>
|
||||
<artifactId>xmlsec</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.opensaml</groupId>
|
||||
<artifactId>opensaml</artifactId>
|
||||
<version>1.1b</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>2.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>1.2.17</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jmxri</artifactId>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client;
|
||||
|
||||
/**
|
||||
* Simple enumeration to hold/capture some of the standard request parameters used by the various protocols.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public enum Protocol {
|
||||
|
||||
CAS1("ticket", "service"), CAS2(CAS1), CAS3(CAS2), SAML11("SAMLart", "TARGET");
|
||||
|
||||
private final String artifactParameterName;
|
||||
|
||||
private final String serviceParameterName;
|
||||
|
||||
private Protocol(final String artifactParameterName, final String serviceParameterName) {
|
||||
this.artifactParameterName = artifactParameterName;
|
||||
this.serviceParameterName = serviceParameterName;
|
||||
}
|
||||
|
||||
private Protocol(final Protocol protocol) {
|
||||
this(protocol.getArtifactParameterName(), protocol.getServiceParameterName());
|
||||
}
|
||||
|
||||
public String getArtifactParameterName() {
|
||||
return this.artifactParameterName;
|
||||
}
|
||||
|
||||
public String getServiceParameterName() {
|
||||
return this.serviceParameterName;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,21 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -29,6 +47,6 @@ public interface AttributePrincipal extends Principal, Serializable {
|
|||
* The Map of key/value pairs associated with this principal.
|
||||
* @return the map of key/value pairs associated with this principal.
|
||||
*/
|
||||
Map getAttributes();
|
||||
Map<String, Object> getAttributes();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,45 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Concrete implementation of the AttributePrincipal interface.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AttributePrincipalImpl implements AttributePrincipal {
|
||||
public class AttributePrincipalImpl extends SimplePrincipal implements AttributePrincipal {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AttributePrincipalImpl.class);
|
||||
|
||||
/** Unique Id for Serialization */
|
||||
private static final long serialVersionUID = -8810123156070148535L;
|
||||
|
||||
/** The unique identifier for this principal. */
|
||||
private final String name;
|
||||
private static final long serialVersionUID = -1443182634624927187L;
|
||||
|
||||
/** Map of key/value pairs about this principal. */
|
||||
private final Map attributes;
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
/** The CAS 2 ticket used to retrieve a proxy ticket. */
|
||||
private final String proxyGrantingTicket;
|
||||
|
|
@ -36,7 +53,7 @@ public class AttributePrincipalImpl implements AttributePrincipal {
|
|||
* @param name the unique identifier for the principal.
|
||||
*/
|
||||
public AttributePrincipalImpl(final String name) {
|
||||
this(name, Collections.EMPTY_MAP);
|
||||
this(name, Collections.<String, Object> emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,8 +62,8 @@ public class AttributePrincipalImpl implements AttributePrincipal {
|
|||
* @param name the unique identifier for the principal.
|
||||
* @param attributes the key/value pairs for this principal.
|
||||
*/
|
||||
public AttributePrincipalImpl(final String name, final Map attributes) {
|
||||
this(name, attributes, null, null);
|
||||
public AttributePrincipalImpl(final String name, final Map<String, Object> attributes) {
|
||||
this(name, attributes, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,40 +73,41 @@ public class AttributePrincipalImpl implements AttributePrincipal {
|
|||
* @param proxyGrantingTicket the ticket associated with this principal.
|
||||
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server.
|
||||
*/
|
||||
public AttributePrincipalImpl(final String name, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
|
||||
this(name, Collections.EMPTY_MAP, proxyGrantingTicket, proxyRetriever);
|
||||
public AttributePrincipalImpl(final String name, final String proxyGrantingTicket,
|
||||
final ProxyRetriever proxyRetriever) {
|
||||
this(name, Collections.<String, Object> emptyMap(), proxyGrantingTicket, proxyRetriever);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new principal witht he supplied name, attributes, and proxying capabilities.
|
||||
* Constructs a new principal with the supplied name, attributes, and proxying capabilities.
|
||||
*
|
||||
* @param name the unique identifier for the principal.
|
||||
* @param attributes the key/value pairs for this principal.
|
||||
* @param proxyGrantingTicket the ticket associated with this principal.
|
||||
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server.
|
||||
*/
|
||||
public AttributePrincipalImpl(final String name, final Map attributes, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
|
||||
this.name = name;
|
||||
public AttributePrincipalImpl(final String name, final Map<String, Object> attributes,
|
||||
final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
|
||||
super(name);
|
||||
this.attributes = attributes;
|
||||
this.proxyGrantingTicket = proxyGrantingTicket;
|
||||
this.proxyRetriever = proxyRetriever;
|
||||
|
||||
CommonUtils.assertNotNull(this.name, "name cannot be null.");
|
||||
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
|
||||
}
|
||||
|
||||
public Map getAttributes() {
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
public String getProxyTicketFor(String service) {
|
||||
@Override
|
||||
public String getProxyTicketFor(final String service) {
|
||||
if (proxyGrantingTicket != null) {
|
||||
return this.proxyRetriever.getProxyTicketIdFor(this.proxyGrantingTicket, service);
|
||||
}
|
||||
|
||||
LOGGER.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved.");
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,28 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
|
@ -18,6 +34,8 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Filter implementation to intercept all requests and attempt to authenticate
|
||||
|
|
@ -28,18 +46,16 @@ import java.io.IOException;
|
|||
* <li><code>casServerLoginUrl</code> - the url to log into CAS, i.e. https://cas.rutgers.edu/login</li>
|
||||
* <li><code>renew</code> - true/false on whether to use renew or not.</li>
|
||||
* <li><code>gateway</code> - true/false on whether to use gateway or not.</li>
|
||||
* <li><code>method</code> - the method used by the CAS server to send the user back to the application (redirect or post).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Please see AbstractCasFilter for additional properties.</p>
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11768 $ $Date: 2007-02-07 15:44:16 -0500 (Wed, 07 Feb 2007) $
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AuthenticationFilter extends AbstractCasFilter {
|
||||
|
||||
public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";
|
||||
|
||||
/**
|
||||
* The URL to the CAS Server login.
|
||||
*/
|
||||
|
|
@ -55,52 +71,155 @@ public class AuthenticationFilter extends AbstractCasFilter {
|
|||
*/
|
||||
private boolean gateway = false;
|
||||
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
super.initInternal(filterConfig);
|
||||
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
|
||||
setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
setGateway(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
|
||||
/**
|
||||
* The method used by the CAS server to send the user back to the application.
|
||||
*/
|
||||
private String method;
|
||||
|
||||
private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
|
||||
|
||||
private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
|
||||
|
||||
private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
|
||||
|
||||
private String internalIp = null;
|
||||
|
||||
private static final String X_REAL_IP = "x-real-ip";
|
||||
|
||||
private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES =
|
||||
new HashMap<String, Class<? extends UrlPatternMatcherStrategy>>();
|
||||
|
||||
static {
|
||||
PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
|
||||
PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
|
||||
PATTERN_MATCHER_TYPES.put("FULL_REGEX", EntireRegionRegexUrlPatternMatcherStrategy.class);
|
||||
PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
|
||||
}
|
||||
|
||||
public AuthenticationFilter() {
|
||||
this(Protocol.CAS2);
|
||||
}
|
||||
|
||||
protected AuthenticationFilter(final Protocol protocol) {
|
||||
super(protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
if (!isIgnoreInitConfiguration()) {
|
||||
super.initInternal(filterConfig);
|
||||
|
||||
final String loginUrl = getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL);
|
||||
if (loginUrl != null) {
|
||||
setCasServerLoginUrl(loginUrl);
|
||||
} else {
|
||||
setCasServerUrlPrefix(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX));
|
||||
}
|
||||
|
||||
setRenew(getBoolean(ConfigurationKeys.RENEW));
|
||||
setGateway(getBoolean(ConfigurationKeys.GATEWAY));
|
||||
setMethod(getString(ConfigurationKeys.METHOD));
|
||||
setInternalIp(getString(ConfigurationKeys.INTERNAL_IP));
|
||||
|
||||
final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN);
|
||||
final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE);
|
||||
|
||||
if (ignorePattern != null) {
|
||||
final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
|
||||
if (ignoreUrlMatcherClass != null) {
|
||||
this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName());
|
||||
} else {
|
||||
try {
|
||||
logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType);
|
||||
this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, e);
|
||||
}
|
||||
}
|
||||
if (this.ignoreUrlPatternMatcherStrategyClass != null) {
|
||||
this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
|
||||
}
|
||||
}
|
||||
|
||||
final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
|
||||
|
||||
if (gatewayStorageClass != null) {
|
||||
setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
|
||||
}
|
||||
|
||||
final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
|
||||
|
||||
if (authenticationRedirectStrategyClass != null) {
|
||||
this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
|
||||
|
||||
final String message = String.format(
|
||||
"one of %s and %s must not be null.",
|
||||
ConfigurationKeys.CAS_SERVER_LOGIN_URL.getName(),
|
||||
ConfigurationKeys.CAS_SERVER_URL_PREFIX.getName());
|
||||
|
||||
CommonUtils.assertNotNull(this.casServerLoginUrl, message);
|
||||
}
|
||||
|
||||
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
@Override
|
||||
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
final HttpSession session = request.getSession(false);
|
||||
final String ticket = request.getParameter(getArtifactParameterName());
|
||||
final Assertion assertion = session != null ? (Assertion) session
|
||||
.getAttribute(CONST_CAS_ASSERTION) : null;
|
||||
final boolean wasGatewayed = session != null
|
||||
&& session.getAttribute(CONST_CAS_GATEWAY) != null;
|
||||
|
||||
if (CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed) {
|
||||
log.debug("no ticket and no assertion found");
|
||||
if (this.gateway) {
|
||||
log.debug("setting gateway attribute in session");
|
||||
request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes");
|
||||
}
|
||||
|
||||
final String serviceUrl = constructServiceUrl(request, response);
|
||||
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, this.gateway);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
|
||||
}
|
||||
|
||||
response.sendRedirect(urlToRedirectTo);
|
||||
if (isInternalRequest(request)) {
|
||||
logger.debug("Request is ignored [internal].");
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session != null) {
|
||||
log.debug("removing gateway attribute from session");
|
||||
session.setAttribute(CONST_CAS_GATEWAY, null);
|
||||
if (isRequestUrlExcluded(request)) {
|
||||
logger.debug("Request is ignored.");
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
final HttpSession session = request.getSession(false);
|
||||
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
|
||||
|
||||
if (assertion != null) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
final String serviceUrl = constructServiceUrl(request, response);
|
||||
final String ticket = retrieveTicketFromRequest(request);
|
||||
final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
|
||||
|
||||
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
final String modifiedServiceUrl;
|
||||
|
||||
logger.debug("no ticket and no assertion found");
|
||||
if (this.gateway) {
|
||||
logger.debug("setting gateway attribute in session");
|
||||
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
|
||||
} else {
|
||||
modifiedServiceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
logger.debug("Constructed service url: {}", modifiedServiceUrl);
|
||||
|
||||
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
|
||||
getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway, this.method);
|
||||
|
||||
logger.debug("redirecting to \"{}\"", urlToRedirectTo);
|
||||
this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
|
||||
}
|
||||
|
||||
public final void setRenew(final boolean renew) {
|
||||
|
|
@ -111,7 +230,52 @@ public class AuthenticationFilter extends AbstractCasFilter {
|
|||
this.gateway = gateway;
|
||||
}
|
||||
|
||||
public void setMethod(final String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public final void setCasServerUrlPrefix(final String casServerUrlPrefix) {
|
||||
setCasServerLoginUrl(CommonUtils.addTrailingSlash(casServerUrlPrefix) + "login");
|
||||
}
|
||||
|
||||
public final void setCasServerLoginUrl(final String casServerLoginUrl) {
|
||||
this.casServerLoginUrl = casServerLoginUrl;
|
||||
}
|
||||
|
||||
public void setInternalIp(String internalIp) {
|
||||
this.internalIp = internalIp;
|
||||
}
|
||||
|
||||
public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
|
||||
this.gatewayStorage = gatewayStorage;
|
||||
}
|
||||
|
||||
private boolean isInternalRequest(final HttpServletRequest request) {
|
||||
if (this.internalIp == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String realIp = request.getHeader(X_REAL_IP);
|
||||
|
||||
return this.internalIp.equals(realIp);
|
||||
}
|
||||
|
||||
private boolean isRequestUrlExcluded(final HttpServletRequest request) {
|
||||
if (this.ignoreUrlPatternMatcherStrategyClass == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StringBuffer urlBuffer = request.getRequestURL();
|
||||
if (request.getQueryString() != null) {
|
||||
urlBuffer.append("?").append(request.getQueryString());
|
||||
}
|
||||
final String requestUri = urlBuffer.toString();
|
||||
return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
|
||||
}
|
||||
|
||||
public final void setIgnoreUrlPatternMatcherStrategyClass(
|
||||
final UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass) {
|
||||
this.ignoreUrlPatternMatcherStrategyClass = ignoreUrlPatternMatcherStrategyClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Interface to abstract the authentication strategy for redirecting. The traditional method was to always just redirect,
|
||||
* but due to AJAX, etc. we may need to support other strategies. This interface is designed to hold that logic such that
|
||||
* authentication filter class does not get crazily complex.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public interface AuthenticationRedirectStrategy {
|
||||
|
||||
/**
|
||||
* Method name is a bit of a misnomer. This method handles "redirection" for a localized version of redirection (i.e. AJAX might mean an XML fragment that contains the url to go to).
|
||||
*
|
||||
* @param request the original HttpServletRequest. MAY NOT BE NULL.
|
||||
* @param response the original HttpServletResponse. MAY NOT BE NULL.
|
||||
* @param potentialRedirectUrl the url that might be used (there are no guarantees of course!)
|
||||
* @throws IOException the exception to throw if there is some type of error. This will bubble up through the filter.
|
||||
*/
|
||||
void redirect(HttpServletRequest request, HttpServletResponse response, String potentialRedirectUrl)
|
||||
throws IOException;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
/**
|
||||
* A pattern matcher that looks inside the url to find the exact pattern specified.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public final class ContainsPatternUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
|
||||
|
||||
private String pattern;
|
||||
|
||||
@Override
|
||||
public boolean matches(final String url) {
|
||||
return url.contains(this.pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPattern(final String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link AuthenticationRedirectStrategy} class that preserves the original behavior that existed prior to 3.3.0.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public final class DefaultAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy {
|
||||
|
||||
@Override
|
||||
public void redirect(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final String potentialRedirectUrl) throws IOException {
|
||||
response.sendRedirect(potentialRedirectUrl);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
public final class DefaultGatewayResolverImpl implements GatewayResolver {
|
||||
|
||||
public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_";
|
||||
|
||||
@Override
|
||||
public boolean hasGatewayedAlready(final HttpServletRequest request, final String serviceUrl) {
|
||||
final HttpSession session = request.getSession(false);
|
||||
|
||||
if (session == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean result = session.getAttribute(CONST_CAS_GATEWAY) != null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String storeGatewayInformation(final HttpServletRequest request, final String serviceUrl) {
|
||||
request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes");
|
||||
return serviceUrl;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A pattern matcher that looks inside the url to find the pattern, that
|
||||
* is assumed to have been specified via regular expressions syntax.
|
||||
* The match behavior is based on {@link Matcher#matches()}:
|
||||
* Attempts to match the entire region against the pattern.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.5
|
||||
*/
|
||||
public final class EntireRegionRegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
|
||||
|
||||
private Pattern pattern;
|
||||
|
||||
public EntireRegionRegexUrlPatternMatcherStrategy() {
|
||||
}
|
||||
|
||||
public EntireRegionRegexUrlPatternMatcherStrategy(final String pattern) {
|
||||
this.setPattern(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final String url) {
|
||||
return this.pattern.matcher(url).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPattern(final String pattern) {
|
||||
this.pattern = Pattern.compile(pattern);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
/**
|
||||
* A pattern matcher that produces a successful match if the pattern
|
||||
* specified matches the given url exactly and equally.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public final class ExactUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
|
||||
|
||||
private String pattern;
|
||||
|
||||
public ExactUrlPatternMatcherStrategy() {}
|
||||
|
||||
public ExactUrlPatternMatcherStrategy(final String pattern) {
|
||||
this.setPattern(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final String url) {
|
||||
return url.equals(this.pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPattern(final String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Implementation of the redirect strategy that can handle a Faces Ajax request in addition to the standard redirect style.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public final class FacesCompatibleAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy {
|
||||
|
||||
private static final String FACES_PARTIAL_AJAX_PARAMETER = "javax.faces.partial.ajax";
|
||||
|
||||
@Override
|
||||
public void redirect(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final String potentialRedirectUrl) throws IOException {
|
||||
|
||||
if (CommonUtils.isNotBlank(request.getParameter(FACES_PARTIAL_AJAX_PARAMETER))) {
|
||||
// this is an ajax request - redirect ajaxly
|
||||
response.setContentType("text/xml");
|
||||
response.setStatus(200);
|
||||
|
||||
final PrintWriter writer = response.getWriter();
|
||||
writer.write("<?xml version='1.0' encoding='UTF-8'?>");
|
||||
writer.write(String.format("<partial-response><redirect url=\"%s\"></redirect></partial-response>",
|
||||
potentialRedirectUrl));
|
||||
} else {
|
||||
response.sendRedirect(potentialRedirectUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Implementations of this should only have a default constructor if
|
||||
* you plan on constructing them via the web.xml.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
public interface GatewayResolver {
|
||||
|
||||
/**
|
||||
* Determines if the request has been gatewayed already. Should also do gateway clean up.
|
||||
*
|
||||
* @param request the Http Servlet Request
|
||||
* @param serviceUrl the service url
|
||||
* @return true if yes, false otherwise.
|
||||
*/
|
||||
boolean hasGatewayedAlready(HttpServletRequest request, String serviceUrl);
|
||||
|
||||
/**
|
||||
* Storage the request for gatewaying and return the service url, which can be modified.
|
||||
*
|
||||
* @param request the HttpServletRequest.
|
||||
* @param serviceUrl the service url
|
||||
* @return the potentially modified service url to redirect to
|
||||
*/
|
||||
String storeGatewayInformation(HttpServletRequest request, String serviceUrl);
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A pattern matcher that looks inside the url to find the pattern, that
|
||||
* is assumed to have been specified via regular expressions syntax.
|
||||
* The match behavior is based on {@link Matcher#find()}:
|
||||
* Attempts to find the next subsequence of the input sequence that matches
|
||||
* the pattern. This method starts at the beginning of this matcher's region, or, if
|
||||
* a previous invocation of the method was successful and the matcher has
|
||||
* not since been reset, at the first character not matched by the previous
|
||||
* match.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public final class RegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
|
||||
|
||||
private Pattern pattern;
|
||||
|
||||
public RegexUrlPatternMatcherStrategy() {
|
||||
}
|
||||
|
||||
public RegexUrlPatternMatcherStrategy(final String pattern) {
|
||||
this.setPattern(pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final String url) {
|
||||
return this.pattern.matcher(url).find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPattern(final String pattern) {
|
||||
this.pattern = Pattern.compile(pattern);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple security group implementation
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public final class SimpleGroup extends SimplePrincipal implements Group {
|
||||
|
||||
/** SimpleGroup.java */
|
||||
private static final long serialVersionUID = 4382154818494550205L;
|
||||
|
||||
/** Group members */
|
||||
private final Set<Principal> members = new HashSet<Principal>();
|
||||
|
||||
/**
|
||||
* Creates a new group with the given name.
|
||||
* @param name Group name.
|
||||
*/
|
||||
public SimpleGroup(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addMember(final Principal user) {
|
||||
return this.members.add(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMember(final Principal member) {
|
||||
return this.members.contains(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<? extends Principal> members() {
|
||||
return Collections.enumeration(this.members);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeMember(final Principal user) {
|
||||
return this.members.remove(user);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString() + ": " + members.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Simple security principal implementation.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class SimplePrincipal implements Principal, Serializable {
|
||||
|
||||
/** SimplePrincipal.java */
|
||||
private static final long serialVersionUID = -5645357206342793145L;
|
||||
|
||||
/** The unique identifier for this principal. */
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Creates a new principal with the given name.
|
||||
* @param name Principal name.
|
||||
*/
|
||||
public SimplePrincipal(final String name) {
|
||||
this.name = name;
|
||||
CommonUtils.assertNotNull(this.name, "name cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
} else if (!(o instanceof SimplePrincipal)) {
|
||||
return false;
|
||||
} else {
|
||||
return getName().equals(((SimplePrincipal) o).getName());
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 37 * getName().hashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
/**
|
||||
* Defines an abstraction by which request urls can be matches against a given pattern.
|
||||
* New instances for all extensions for this strategy interface will be created per
|
||||
* each request. The client will ultimately invoke the {@link #matches(String)} method
|
||||
* having already applied and set the pattern via the {@link #setPattern(String)} method.
|
||||
* The pattern itself will be retrieved via the client configuration.
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public interface UrlPatternMatcherStrategy {
|
||||
/**
|
||||
* Execute the match between the given pattern and the url
|
||||
* @param url the request url typically with query strings included
|
||||
* @return true if match is successful
|
||||
*/
|
||||
boolean matches(String url);
|
||||
|
||||
/**
|
||||
* The pattern against which the url is compared
|
||||
* @param pattern
|
||||
*/
|
||||
void setPattern(String pattern);
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Base class to provide most of the boiler-plate code (i.e. checking for proper values, returning defaults, etc.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public abstract class BaseConfigurationStrategy implements ConfigurationStrategy {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
|
||||
return getValue(configurationKey, new Parser<Boolean>() {
|
||||
@Override
|
||||
public Boolean parse(final String value) {
|
||||
return CommonUtils.toBoolean(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long getLong(final ConfigurationKey<Long> configurationKey) {
|
||||
return getValue(configurationKey, new Parser<Long>() {
|
||||
@Override
|
||||
public Long parse(final String value) {
|
||||
return CommonUtils.toLong(value, configurationKey.getDefaultValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getInt(final ConfigurationKey<Integer> configurationKey) {
|
||||
return getValue(configurationKey, new Parser<Integer>() {
|
||||
@Override
|
||||
public Integer parse(final String value) {
|
||||
return CommonUtils.toInt(value, configurationKey.getDefaultValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getString(final ConfigurationKey<String> configurationKey) {
|
||||
return getValue(configurationKey, new Parser<String>() {
|
||||
@Override
|
||||
public String parse(final String value) {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
|
||||
return getValue(configurationKey, new Parser<Class<? extends T>>() {
|
||||
@Override
|
||||
public Class<? extends T> parse(final String value) {
|
||||
try {
|
||||
return ReflectUtils.loadClass(value);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
return configurationKey.getDefaultValue();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T> T getValue(final ConfigurationKey<T> configurationKey, final Parser<T> parser) {
|
||||
final String value = getWithCheck(configurationKey);
|
||||
|
||||
if (CommonUtils.isBlank(value)) {
|
||||
logger.trace("No value found for property {}, returning default {}", configurationKey.getName(), configurationKey.getDefaultValue());
|
||||
return configurationKey.getDefaultValue();
|
||||
} else {
|
||||
logger.trace("Loaded property {} with value {}", configurationKey.getName(), configurationKey.getDefaultValue());
|
||||
}
|
||||
|
||||
return parser.parse(value);
|
||||
}
|
||||
|
||||
private String getWithCheck(final ConfigurationKey configurationKey) {
|
||||
CommonUtils.assertNotNull(configurationKey, "configurationKey cannot be null");
|
||||
|
||||
return get(configurationKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the String value for this key. Returns null if there is no value.
|
||||
*
|
||||
* @param configurationKey the key to retrieve. MUST NOT BE NULL.
|
||||
* @return the String if its found, null otherwise.
|
||||
*/
|
||||
protected abstract String get(ConfigurationKey configurationKey);
|
||||
|
||||
private interface Parser<T> {
|
||||
|
||||
T parse(String value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Holder class to represent a particular configuration key and its optional default value.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class ConfigurationKey<E> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final E defaultValue;
|
||||
|
||||
public ConfigurationKey(final String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
public ConfigurationKey(final String name, final E defaultValue) {
|
||||
CommonUtils.assertNotNull(name, "name must not be null.");
|
||||
this.name = name;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The referencing name of the configuration key (i.e. what you would use to look it up in your configuration strategy)
|
||||
*
|
||||
* @return the name. MUST NOT BE NULL.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The (optional) default value to use when this configuration key is not set. If a value is provided it should be used. A <code>null</code> value indicates that there is no default.
|
||||
*
|
||||
* @return the default value or null.
|
||||
*/
|
||||
public E getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;
|
||||
import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
|
||||
import org.jasig.cas.client.authentication.GatewayResolver;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
||||
/**
|
||||
* Holder interface for all known configuration keys.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public interface ConfigurationKeys {
|
||||
|
||||
ConfigurationKey<String> ARTIFACT_PARAMETER_NAME = new ConfigurationKey<String>("artifactParameterName", Protocol.CAS2.getArtifactParameterName());
|
||||
ConfigurationKey<String> SERVER_NAME = new ConfigurationKey<String>("serverName", null);
|
||||
ConfigurationKey<String> SERVICE = new ConfigurationKey<String>("service");
|
||||
ConfigurationKey<Boolean> RENEW = new ConfigurationKey<Boolean>("renew", Boolean.FALSE);
|
||||
ConfigurationKey<String> LOGOUT_PARAMETER_NAME = new ConfigurationKey<String>("logoutParameterName", "logoutRequest");
|
||||
ConfigurationKey<Boolean> ARTIFACT_PARAMETER_OVER_POST = new ConfigurationKey<Boolean>("artifactParameterOverPost", Boolean.FALSE);
|
||||
ConfigurationKey<Boolean> EAGERLY_CREATE_SESSIONS = new ConfigurationKey<Boolean>("eagerlyCreateSessions", Boolean.TRUE);
|
||||
ConfigurationKey<Boolean> ENCODE_SERVICE_URL = new ConfigurationKey<Boolean>("encodeServiceUrl", Boolean.TRUE);
|
||||
ConfigurationKey<String> SSL_CONFIG_FILE = new ConfigurationKey<String>("sslConfigFile", null);
|
||||
ConfigurationKey<String> ROLE_ATTRIBUTE = new ConfigurationKey<String>("roleAttribute", null);
|
||||
ConfigurationKey<Boolean> IGNORE_CASE = new ConfigurationKey<Boolean>("ignoreCase", Boolean.FALSE);
|
||||
ConfigurationKey<String> CAS_SERVER_LOGIN_URL = new ConfigurationKey<String>("casServerLoginUrl", null);
|
||||
ConfigurationKey<Boolean> GATEWAY = new ConfigurationKey<Boolean>("gateway", Boolean.FALSE);
|
||||
ConfigurationKey<String> METHOD = new ConfigurationKey<String>("method", null);
|
||||
ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>> AUTHENTICATION_REDIRECT_STRATEGY_CLASS = new ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>>("authenticationRedirectStrategyClass", null);
|
||||
ConfigurationKey<Class<? extends GatewayResolver>> GATEWAY_STORAGE_CLASS = new ConfigurationKey<Class<? extends GatewayResolver>>("gatewayStorageClass", DefaultGatewayResolverImpl.class);
|
||||
ConfigurationKey<String> CAS_SERVER_URL_PREFIX = new ConfigurationKey<String>("casServerUrlPrefix", null);
|
||||
ConfigurationKey<String> ENCODING = new ConfigurationKey<String>("encoding", null);
|
||||
ConfigurationKey<Long> TOLERANCE = new ConfigurationKey<Long>("tolerance", 1000L);
|
||||
ConfigurationKey<String> PRIVATE_KEY_PATH = new ConfigurationKey<String>("privateKeyPath", null);
|
||||
ConfigurationKey<String> PRIVATE_KEY_ALGORITHM = new ConfigurationKey<String>("privateKeyAlgorithm", "RSA");
|
||||
|
||||
/**
|
||||
* @deprecated As of 3.4. This constant is not used by the client and will
|
||||
* be removed in future versions.
|
||||
*/
|
||||
@Deprecated
|
||||
ConfigurationKey<Boolean> DISABLE_XML_SCHEMA_VALIDATION = new ConfigurationKey<Boolean>("disableXmlSchemaValidation", Boolean.FALSE);
|
||||
ConfigurationKey<String> INTERNAL_IP = new ConfigurationKey<String>("internalIp", null);
|
||||
ConfigurationKey<String> IGNORE_PATTERN = new ConfigurationKey<String>("ignorePattern", null);
|
||||
ConfigurationKey<String> IGNORE_URL_PATTERN_TYPE = new ConfigurationKey<String>("ignoreUrlPatternType", "REGEX");
|
||||
ConfigurationKey<Class<? extends HostnameVerifier>> HOSTNAME_VERIFIER = new ConfigurationKey<Class<? extends HostnameVerifier>>("hostnameVerifier", null);
|
||||
ConfigurationKey<String> HOSTNAME_VERIFIER_CONFIG = new ConfigurationKey<String>("hostnameVerifierConfig", null);
|
||||
ConfigurationKey<Boolean> EXCEPTION_ON_VALIDATION_FAILURE = new ConfigurationKey<Boolean>("exceptionOnValidationFailure", Boolean.TRUE);
|
||||
ConfigurationKey<Boolean> REDIRECT_AFTER_VALIDATION = new ConfigurationKey<Boolean>("redirectAfterValidation", Boolean.TRUE);
|
||||
ConfigurationKey<Boolean> USE_SESSION = new ConfigurationKey<Boolean>("useSession", Boolean.TRUE);
|
||||
ConfigurationKey<String> SECRET_KEY = new ConfigurationKey<String>("secretKey", null);
|
||||
ConfigurationKey<String> CIPHER_ALGORITHM = new ConfigurationKey<String>("cipherAlgorithm", "DESede");
|
||||
ConfigurationKey<String> PROXY_RECEPTOR_URL = new ConfigurationKey<String>("proxyReceptorUrl", null);
|
||||
ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>> PROXY_GRANTING_TICKET_STORAGE_CLASS = new ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>>("proxyGrantingTicketStorageClass", ProxyGrantingTicketStorageImpl.class);
|
||||
ConfigurationKey<Integer> MILLIS_BETWEEN_CLEAN_UPS = new ConfigurationKey<Integer>("millisBetweenCleanUps", 60000);
|
||||
ConfigurationKey<Boolean> ACCEPT_ANY_PROXY = new ConfigurationKey<Boolean>("acceptAnyProxy", Boolean.FALSE);
|
||||
ConfigurationKey<String> ALLOWED_PROXY_CHAINS = new ConfigurationKey<String>("allowedProxyChains", null);
|
||||
ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>> TICKET_VALIDATOR_CLASS = new ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>>("ticketValidatorClass", null);
|
||||
ConfigurationKey<String> PROXY_CALLBACK_URL = new ConfigurationKey<String>("proxyCallbackUrl", null);
|
||||
ConfigurationKey<String> RELAY_STATE_PARAMETER_NAME = new ConfigurationKey<String>("relayStateParameterName", "RelayState");
|
||||
ConfigurationKey<String> LOGOUT_CALLBACK_PATH = new ConfigurationKey<String>("logoutCallbackPath", null);
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Abstraction to allow for pluggable methods for retrieving filter configuration.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public interface ConfigurationStrategy {
|
||||
|
||||
/**
|
||||
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
|
||||
*
|
||||
* @param configurationKey the configuration key. MUST NOT BE NULL.
|
||||
* @return the configured value, or the default value.
|
||||
*/
|
||||
boolean getBoolean(ConfigurationKey<Boolean> configurationKey);
|
||||
|
||||
/**
|
||||
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
|
||||
*
|
||||
* @param configurationKey the configuration key. MUST NOT BE NULL.
|
||||
* @return the configured value, or the default value.
|
||||
*/
|
||||
String getString(ConfigurationKey<String> configurationKey);
|
||||
|
||||
/**
|
||||
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
|
||||
*
|
||||
* @param configurationKey the configuration key. MUST NOT BE NULL.
|
||||
* @return the configured value, or the default value.
|
||||
*/
|
||||
long getLong(ConfigurationKey<Long> configurationKey);
|
||||
|
||||
/**
|
||||
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
|
||||
*
|
||||
* @param configurationKey the configuration key. MUST NOT BE NULL.
|
||||
* @return the configured value, or the default value.
|
||||
*/
|
||||
int getInt(ConfigurationKey<Integer> configurationKey);
|
||||
|
||||
/**
|
||||
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
|
||||
*
|
||||
* @param configurationKey the configuration key. MUST NOT BE NULL.
|
||||
* @return the configured value, or the default value.
|
||||
*/
|
||||
<T> Class<? extends T> getClass(ConfigurationKey<Class<? extends T>> configurationKey);
|
||||
|
||||
/**
|
||||
* Initializes the strategy. This must be called before calling any of the "get" methods.
|
||||
*
|
||||
* @param filterConfig the filter configuration object.
|
||||
* @param filterClazz the filter
|
||||
*/
|
||||
void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz);
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Enumeration to map simple names to the underlying classes so that deployers can reference the simple name in the
|
||||
* <code>web.xml</code> instead of the fully qualified class name.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public enum ConfigurationStrategyName {
|
||||
|
||||
DEFAULT(LegacyConfigurationStrategyImpl.class), JNDI(JndiConfigurationStrategyImpl.class), WEB_XML(WebXmlConfigurationStrategyImpl.class),
|
||||
PROPERTY_FILE(PropertiesConfigurationStrategyImpl.class), SYSTEM_PROPERTIES(SystemPropertiesConfigurationStrategyImpl.class);
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationStrategyName.class);
|
||||
|
||||
private final Class<? extends ConfigurationStrategy> configurationStrategyClass;
|
||||
|
||||
private ConfigurationStrategyName(final Class<? extends ConfigurationStrategy> configurationStrategyClass) {
|
||||
this.configurationStrategyClass = configurationStrategyClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper method that will resolve a simple string to either an enum value or a {@link org.jasig.cas.client.configuration.ConfigurationStrategy} class.
|
||||
*
|
||||
* @param value the value to attempt to resolve.
|
||||
* @return the underlying class that this maps to (either via simple name or fully qualified class name).
|
||||
*/
|
||||
public static Class<? extends ConfigurationStrategy> resolveToConfigurationStrategy(final String value) {
|
||||
if (CommonUtils.isBlank(value)) {
|
||||
return DEFAULT.configurationStrategyClass;
|
||||
}
|
||||
|
||||
for (final ConfigurationStrategyName csn : values()) {
|
||||
if (csn.name().equalsIgnoreCase(value)) {
|
||||
return csn.configurationStrategyClass;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final Class<?> clazz = Class.forName(value);
|
||||
|
||||
if (ConfigurationStrategy.class.isAssignableFrom(clazz)) {
|
||||
return (Class<? extends ConfigurationStrategy>) clazz;
|
||||
}
|
||||
} catch (final ClassNotFoundException e) {
|
||||
LOGGER.error("Unable to locate strategy {} by name or class name. Using default strategy instead.", value, e);
|
||||
}
|
||||
|
||||
return DEFAULT.configurationStrategyClass;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Loads configuration information from JNDI, using the <code>defaultValue</code> if it can't.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class JndiConfigurationStrategyImpl extends BaseConfigurationStrategy {
|
||||
|
||||
private static final String ENVIRONMENT_PREFIX = "java:comp/env/cas/";
|
||||
|
||||
private final String environmentPrefix;
|
||||
|
||||
private InitialContext context;
|
||||
|
||||
private String simpleFilterName;
|
||||
|
||||
public JndiConfigurationStrategyImpl() {
|
||||
this(ENVIRONMENT_PREFIX);
|
||||
}
|
||||
|
||||
public JndiConfigurationStrategyImpl(final String environmentPrefix) {
|
||||
this.environmentPrefix = environmentPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final String get(final ConfigurationKey configurationKey) {
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String propertyName = configurationKey.getName();
|
||||
final String filterValue = loadFromContext(context, this.environmentPrefix + this.simpleFilterName + "/" + propertyName);
|
||||
|
||||
if (CommonUtils.isNotBlank(filterValue)) {
|
||||
logger.info("Property [{}] loaded from JNDI Filter Specific Property with value [{}]", propertyName, filterValue);
|
||||
return filterValue;
|
||||
}
|
||||
|
||||
final String rootValue = loadFromContext(context, this.environmentPrefix + propertyName);
|
||||
|
||||
if (CommonUtils.isNotBlank(rootValue)) {
|
||||
logger.info("Property [{}] loaded from JNDI with value [{}]", propertyName, rootValue);
|
||||
return rootValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String loadFromContext(final InitialContext context, final String path) {
|
||||
try {
|
||||
return (String) context.lookup(path);
|
||||
} catch (final NamingException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
|
||||
this.simpleFilterName = clazz.getSimpleName();
|
||||
try {
|
||||
this.context = new InitialContext();
|
||||
} catch (final NamingException e) {
|
||||
logger.error("Unable to create InitialContext. No properties can be loaded via JNDI.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Replicates the original behavior by checking the {@link org.jasig.cas.client.configuration.WebXmlConfigurationStrategyImpl} first, and then
|
||||
* the {@link org.jasig.cas.client.configuration.JndiConfigurationStrategyImpl} before using the <code>defaultValue</code>.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class LegacyConfigurationStrategyImpl extends BaseConfigurationStrategy {
|
||||
|
||||
private final WebXmlConfigurationStrategyImpl webXmlConfigurationStrategy = new WebXmlConfigurationStrategyImpl();
|
||||
|
||||
private final JndiConfigurationStrategyImpl jndiConfigurationStrategy = new JndiConfigurationStrategyImpl();
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
|
||||
this.webXmlConfigurationStrategy.init(filterConfig, filterClazz);
|
||||
this.jndiConfigurationStrategy.init(filterConfig, filterClazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String get(final ConfigurationKey key) {
|
||||
final String value1 = this.webXmlConfigurationStrategy.get(key);
|
||||
|
||||
if (CommonUtils.isNotBlank(value1)) {
|
||||
return value1;
|
||||
}
|
||||
|
||||
return this.jndiConfigurationStrategy.get(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class PropertiesConfigurationStrategyImpl extends BaseConfigurationStrategy {
|
||||
|
||||
/**
|
||||
* Property name we'll use in the {@link javax.servlet.FilterConfig} and {@link javax.servlet.ServletConfig} to try and find where
|
||||
* you stored the configuration file.
|
||||
*/
|
||||
private static final String CONFIGURATION_FILE_LOCATION = "configFileLocation";
|
||||
|
||||
/**
|
||||
* Default location of the configuration file. Mostly for testing/demo. You will most likely want to configure an alternative location.
|
||||
*/
|
||||
private static final String DEFAULT_CONFIGURATION_FILE_LOCATION = "/etc/java-cas-client.properties";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesConfigurationStrategyImpl.class);
|
||||
|
||||
private String simpleFilterName;
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
@Override
|
||||
protected String get(final ConfigurationKey configurationKey) {
|
||||
final String property = configurationKey.getName();
|
||||
final String filterSpecificProperty = this.simpleFilterName + "." + property;
|
||||
|
||||
final String filterSpecificValue = this.properties.getProperty(filterSpecificProperty);
|
||||
|
||||
if (CommonUtils.isNotEmpty(filterSpecificValue)) {
|
||||
return filterSpecificValue;
|
||||
}
|
||||
|
||||
return this.properties.getProperty(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
|
||||
this.simpleFilterName = filterClazz.getSimpleName();
|
||||
final String fileLocationFromFilterConfig = filterConfig.getInitParameter(CONFIGURATION_FILE_LOCATION);
|
||||
final boolean filterConfigFileLoad = loadPropertiesFromFile(fileLocationFromFilterConfig);
|
||||
|
||||
if (!filterConfigFileLoad) {
|
||||
final String fileLocationFromServletConfig = filterConfig.getServletContext().getInitParameter(CONFIGURATION_FILE_LOCATION);
|
||||
final boolean servletContextFileLoad = loadPropertiesFromFile(fileLocationFromServletConfig);
|
||||
|
||||
if (!servletContextFileLoad) {
|
||||
final boolean defaultConfigFileLoaded = loadPropertiesFromFile(DEFAULT_CONFIGURATION_FILE_LOCATION);
|
||||
CommonUtils.assertTrue(defaultConfigFileLoaded, "unable to load properties to configure CAS client");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean loadPropertiesFromFile(final String file) {
|
||||
if (CommonUtils.isEmpty(file)) {
|
||||
return false;
|
||||
}
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
this.properties.load(fis);
|
||||
return true;
|
||||
} catch (final IOException e) {
|
||||
LOGGER.warn("Unable to load properties for file {}", file, e);
|
||||
return false;
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(fis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Load all configuration from system properties.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class SystemPropertiesConfigurationStrategyImpl extends BaseConfigurationStrategy {
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String get(final ConfigurationKey configurationKey) {
|
||||
return System.getProperty(configurationKey.getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.configuration;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.jasig.cas.client.configuration.ConfigurationStrategy} that first checks the {@link javax.servlet.FilterConfig} and
|
||||
* then checks the {@link javax.servlet.ServletContext}, ultimately falling back to the <code>defaultValue</code>.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final class WebXmlConfigurationStrategyImpl extends BaseConfigurationStrategy {
|
||||
|
||||
private FilterConfig filterConfig;
|
||||
|
||||
@Override
|
||||
protected String get(final ConfigurationKey configurationKey) {
|
||||
final String value = this.filterConfig.getInitParameter(configurationKey.getName());
|
||||
|
||||
if (CommonUtils.isNotBlank(value)) {
|
||||
CommonUtils.assertFalse(ConfigurationKeys.RENEW.equals(configurationKey), "Renew MUST be specified via context parameter or JNDI environment to avoid misconfiguration.");
|
||||
logger.info("Property [{}] loaded from FilterConfig.getInitParameter with value [{}]", configurationKey, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
final String value2 = filterConfig.getServletContext().getInitParameter(configurationKey.getName());
|
||||
|
||||
if (CommonUtils.isNotBlank(value2)) {
|
||||
logger.info("Property [{}] loaded from ServletContext.getInitParameter with value [{}]", configurationKey,
|
||||
value2);
|
||||
return value2;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
|
||||
this.filterConfig = filterConfig;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.jasig.cas.client.authentication.SimplePrincipal;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Principal implementation that contains the CAS ticket validation assertion.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class AssertionPrincipal extends SimplePrincipal implements Serializable {
|
||||
|
||||
/** AssertionPrincipal.java */
|
||||
private static final long serialVersionUID = 2288520214366461693L;
|
||||
|
||||
/** CAS assertion describing authenticated state */
|
||||
private final Assertion assertion;
|
||||
|
||||
/**
|
||||
* Creates a new principal containing the CAS assertion.
|
||||
*
|
||||
* @param name Principal name.
|
||||
* @param assertion CAS assertion.
|
||||
*/
|
||||
public AssertionPrincipal(final String name, final Assertion assertion) {
|
||||
super(name);
|
||||
this.assertion = assertion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CAS ticket validation assertion.
|
||||
*/
|
||||
public Assertion getAssertion() {
|
||||
return this.assertion;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,588 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.*;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
import org.jasig.cas.client.authentication.SimpleGroup;
|
||||
import org.jasig.cas.client.authentication.SimplePrincipal;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* JAAS login module that delegates to a CAS {@link TicketValidator} component
|
||||
* for authentication, and on success populates a {@link Subject} with principal
|
||||
* data including NetID and principal attributes. The module expects to be provided
|
||||
* with the CAS ticket (required) and service (optional) parameters via
|
||||
* {@link PasswordCallback} and {@link NameCallback}, respectively, by the
|
||||
* {@link CallbackHandler} that is part of the JAAS framework in which the servlet
|
||||
* resides.
|
||||
*
|
||||
* <p>
|
||||
* Module configuration options:
|
||||
* <ul>
|
||||
* <li>ticketValidatorClass - Fully-qualified class name of CAS ticket validator class.</li>
|
||||
* <li>casServerUrlPrefix - URL to root of CAS Web application context.</li>
|
||||
* <li>service (optional) - CAS service parameter that may be overridden by callback handler.
|
||||
* NOTE: service must be specified by at least one component such that it is available at
|
||||
* service ticket validation time</li>
|
||||
* <li>defaultRoles (optional) - Comma-delimited list of static roles applied to all
|
||||
* authenticated principals.</li>
|
||||
* <li>roleAttributeNames (optional) - Comma-delimited list of attribute names that describe
|
||||
* role data delivered to CAS in the service-ticket validation response that should be
|
||||
* applied to the current authenticated principal.</li>
|
||||
* <li>principalGroupName (optional) - The name of a group principal containing the
|
||||
* primary principal name of the current JAAS subject. The default value is "CallerPrincipal",
|
||||
* which is suitable for JBoss.</li>
|
||||
* <li>roleGroupName (optional) - The name of a group principal containing all role data.
|
||||
* The default value is "Roles", which is suitable for JBoss.</li>
|
||||
* <li>cacheAssertions (optional) - Flag to enable assertion caching. This may be needed
|
||||
* for JAAS providers that attempt to periodically reauthenticate to renew principal.
|
||||
* Since CAS tickets are one-time-use, a cached assertion must be provided on reauthentication.</li>
|
||||
* <li>cacheTimeout (optional) - Assertion cache timeout in minutes.</li>
|
||||
* <li>cacheTimeoutUnit (optional) - Assertion cache timeout unit. Must be one of {@link TimeUnit} enumeration
|
||||
* names, e.g. DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS. Default unit is MINUTES.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Module options not explicitly listed above are treated as attributes of the
|
||||
* given ticket validator class, e.g. <code>tolerance</code> in the following example.
|
||||
*
|
||||
* <p>
|
||||
* Sample jaas.config file entry for this module:
|
||||
* <pre>
|
||||
* cas {
|
||||
* org.jasig.cas.client.jaas.CasLoginModule required
|
||||
* ticketValidatorClass="org.jasig.cas.client.validation.Saml11TicketValidator"
|
||||
* casServerUrlPrefix="https://cas.example.com/cas"
|
||||
* tolerance="20000"
|
||||
* service="https://webapp.example.com/webapp"
|
||||
* defaultRoles="admin,operator"
|
||||
* roleAttributeNames="memberOf,eduPersonAffiliation"
|
||||
* principalGroupName="CallerPrincipal"
|
||||
* roleGroupName="Roles";
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class CasLoginModule implements LoginModule {
|
||||
/** Constant for login name stored in shared state. */
|
||||
public static final String LOGIN_NAME = "javax.security.auth.login.name";
|
||||
|
||||
/**
|
||||
* Default group name for storing caller principal.
|
||||
* The default value supports JBoss, but is configurable to hopefully
|
||||
* support other JEE containers.
|
||||
*/
|
||||
public static final String DEFAULT_PRINCIPAL_GROUP_NAME = "CallerPrincipal";
|
||||
|
||||
/**
|
||||
* Default group name for storing role membership data.
|
||||
* The default value supports JBoss, but is configurable to hopefully
|
||||
* support other JEE containers.
|
||||
*/
|
||||
public static final String DEFAULT_ROLE_GROUP_NAME = "Roles";
|
||||
|
||||
/**
|
||||
* Default assertion cache timeout in minutes. Default is 8 hours.
|
||||
*/
|
||||
public static final int DEFAULT_CACHE_TIMEOUT = 480;
|
||||
|
||||
/** Default assertion cache timeout unit is minutes. */
|
||||
public static final TimeUnit DEFAULT_CACHE_TIMEOUT_UNIT = TimeUnit.MINUTES;
|
||||
|
||||
/**
|
||||
* Stores mapping of ticket to assertion to support JAAS providers that
|
||||
* attempt to periodically re-authenticate to renew principal. Since
|
||||
* CAS tickets are one-time-use, a cached assertion must be provided on
|
||||
* re-authentication.
|
||||
*/
|
||||
protected static final Map<TicketCredential, Assertion> ASSERTION_CACHE = new HashMap<TicketCredential, Assertion>();
|
||||
|
||||
/** Logger instance */
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/** JAAS authentication subject */
|
||||
protected Subject subject;
|
||||
|
||||
/** JAAS callback handler */
|
||||
protected CallbackHandler callbackHandler;
|
||||
|
||||
/** CAS ticket validator */
|
||||
protected TicketValidator ticketValidator;
|
||||
|
||||
/** CAS service parameter used if no service is provided via TextCallback on login */
|
||||
protected String service;
|
||||
|
||||
/** CAS assertion */
|
||||
protected Assertion assertion;
|
||||
|
||||
/** CAS ticket credential */
|
||||
protected TicketCredential ticket;
|
||||
|
||||
/** Login module shared state */
|
||||
protected Map<String, Object> sharedState;
|
||||
|
||||
/** Roles to be added to all authenticated principals by default */
|
||||
protected String[] defaultRoles;
|
||||
|
||||
/** Names of attributes in the CAS assertion that should be used for role data */
|
||||
protected final Set<String> roleAttributeNames = new HashSet<String>();
|
||||
|
||||
/** Name of JAAS Group containing caller principal */
|
||||
protected String principalGroupName = DEFAULT_PRINCIPAL_GROUP_NAME;
|
||||
|
||||
/** Name of JAAS Group containing role data */
|
||||
protected String roleGroupName = DEFAULT_ROLE_GROUP_NAME;
|
||||
|
||||
/** Enables or disable assertion caching */
|
||||
protected boolean cacheAssertions;
|
||||
|
||||
/** Assertion cache timeout in minutes */
|
||||
protected int cacheTimeout = DEFAULT_CACHE_TIMEOUT;
|
||||
|
||||
/** Units of cache timeout. */
|
||||
protected TimeUnit cacheTimeoutUnit = DEFAULT_CACHE_TIMEOUT_UNIT;
|
||||
|
||||
/**
|
||||
* Initializes the CAS login module.
|
||||
*
|
||||
* @param subject Authentication subject.
|
||||
* @param handler Callback handler.
|
||||
* @param state Shared state map.
|
||||
* @param options Login module options. The following are supported:
|
||||
* <ul>
|
||||
* <li>service - CAS service URL used for service ticket validation.</li>
|
||||
* <li>ticketValidatorClass - fully-qualified class name of service ticket validator component.</li>
|
||||
* <li>defaultRoles (optional) - comma-delimited list of roles to be added to all authenticated principals.</li>
|
||||
* <li>roleAttributeNames (optional) - comma-delimited list of attributes in the CAS assertion that contain role data.</li>
|
||||
* <li>principalGroupName (optional) - name of JAAS Group containing caller principal.</li>
|
||||
* <li>roleGroupName (optional) - name of JAAS Group containing role data</li>
|
||||
* <li>cacheAssertions (optional) - whether or not to cache assertions.
|
||||
* Some JAAS providers attempt to reauthenticate users after an indeterminate
|
||||
* period of time. Since the credential used for authentication is a CAS ticket,
|
||||
* which by default are single use, reauthentication fails. Assertion caching addresses this
|
||||
* behavior.</li>
|
||||
* <li>cacheTimeout (optional) - assertion cache timeout in minutes.</li>
|
||||
* <li>cacheTimeoutUnit (optional) - Assertion cache timeout unit. Must be one of {@link TimeUnit} enumeration
|
||||
* names, e.g. DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS. Default unit is MINUTES.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public final void initialize(final Subject subject, final CallbackHandler handler, final Map<String, ?> state,
|
||||
final Map<String, ?> options) {
|
||||
|
||||
this.assertion = null;
|
||||
this.callbackHandler = handler;
|
||||
this.subject = subject;
|
||||
this.sharedState = (Map<String, Object>) state;
|
||||
this.sharedState = new HashMap<String, Object>(state);
|
||||
|
||||
String ticketValidatorClass = null;
|
||||
|
||||
for (final String key : options.keySet()) {
|
||||
logger.trace("Processing option {}", key);
|
||||
if ("service".equals(key)) {
|
||||
this.service = (String) options.get(key);
|
||||
logger.debug("Set service={}", this.service);
|
||||
} else if ("ticketValidatorClass".equals(key)) {
|
||||
ticketValidatorClass = (String) options.get(key);
|
||||
logger.debug("Set ticketValidatorClass={}", ticketValidatorClass);
|
||||
} else if ("defaultRoles".equals(key)) {
|
||||
final String roles = (String) options.get(key);
|
||||
logger.trace("Got defaultRoles value {}", roles);
|
||||
this.defaultRoles = roles.split(",\\s*");
|
||||
logger.debug("Set defaultRoles={}", Arrays.asList(this.defaultRoles));
|
||||
} else if ("roleAttributeNames".equals(key)) {
|
||||
final String attrNames = (String) options.get(key);
|
||||
logger.trace("Got roleAttributeNames value {}", attrNames);
|
||||
final String[] attributes = attrNames.split(",\\s*");
|
||||
this.roleAttributeNames.addAll(Arrays.asList(attributes));
|
||||
logger.debug("Set roleAttributeNames={}", this.roleAttributeNames);
|
||||
} else if ("principalGroupName".equals(key)) {
|
||||
this.principalGroupName = (String) options.get(key);
|
||||
logger.debug("Set principalGroupName={}", this.principalGroupName);
|
||||
} else if ("roleGroupName".equals(key)) {
|
||||
this.roleGroupName = (String) options.get(key);
|
||||
logger.debug("Set roleGroupName={}", this.roleGroupName);
|
||||
} else if ("cacheAssertions".equals(key)) {
|
||||
this.cacheAssertions = Boolean.parseBoolean((String) options.get(key));
|
||||
logger.debug("Set cacheAssertions={}", this.cacheAssertions);
|
||||
} else if ("cacheTimeout".equals(key)) {
|
||||
this.cacheTimeout = Integer.parseInt((String) options.get(key));
|
||||
logger.debug("Set cacheTimeout={}", this.cacheTimeout);
|
||||
} else if ("cacheTimeoutUnit".equals(key)) {
|
||||
this.cacheTimeoutUnit = Enum.valueOf(TimeUnit.class, (String) options.get(key));
|
||||
logger.debug("Set cacheTimeoutUnit={}", this.cacheTimeoutUnit);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cacheAssertions) {
|
||||
cleanCache();
|
||||
}
|
||||
|
||||
CommonUtils.assertNotNull(ticketValidatorClass, "ticketValidatorClass is required.");
|
||||
this.ticketValidator = createTicketValidator(ticketValidatorClass, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations to perform before doing login.
|
||||
*
|
||||
* @return true if you'd like login to continue, false otherwise.
|
||||
*/
|
||||
protected boolean preLogin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This occurs after logout is processed.
|
||||
*
|
||||
* @param result the result from the login attempt.
|
||||
*/
|
||||
protected void postLogin(final boolean result) {
|
||||
// template method
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean login() throws LoginException {
|
||||
logger.debug("Performing login.");
|
||||
|
||||
if (!preLogin()) {
|
||||
logger.debug("preLogin failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final NameCallback serviceCallback = new NameCallback("service");
|
||||
final PasswordCallback ticketCallback = new PasswordCallback("ticket", false);
|
||||
boolean result = false;
|
||||
try {
|
||||
try {
|
||||
this.callbackHandler.handle(new Callback[] { ticketCallback, serviceCallback });
|
||||
} catch (final IOException e) {
|
||||
logger.info("Login failed due to IO exception in callback handler", e);
|
||||
throw (LoginException) new LoginException("IO exception in callback handler: " + e).initCause(e);
|
||||
} catch (final UnsupportedCallbackException e) {
|
||||
logger.info("Login failed due to unsupported callback", e);
|
||||
throw (LoginException) new LoginException(
|
||||
"Callback handler does not support PasswordCallback and TextInputCallback.").initCause(e);
|
||||
}
|
||||
|
||||
if (ticketCallback.getPassword() != null) {
|
||||
this.ticket = new TicketCredential(new String(ticketCallback.getPassword()));
|
||||
final String service = CommonUtils.isNotBlank(serviceCallback.getName()) ? serviceCallback.getName()
|
||||
: this.service;
|
||||
|
||||
if (this.cacheAssertions) {
|
||||
this.assertion = ASSERTION_CACHE.get(ticket);
|
||||
if (this.assertion != null) {
|
||||
logger.debug("Assertion found in cache.");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.assertion == null) {
|
||||
logger.debug("CAS assertion is null; ticket validation required.");
|
||||
if (CommonUtils.isBlank(service)) {
|
||||
logger.info("Login failed because required CAS service parameter not provided.");
|
||||
throw new LoginException(
|
||||
"Neither login module nor callback handler provided required service parameter.");
|
||||
}
|
||||
try {
|
||||
logger.debug("Attempting ticket validation with service={} and ticket={}", service,
|
||||
this.ticket);
|
||||
this.assertion = this.ticketValidator.validate(this.ticket.getName(), service);
|
||||
|
||||
} catch (final Exception e) {
|
||||
logger.info("Login failed due to CAS ticket validation failure", e);
|
||||
throw (LoginException) new LoginException("CAS ticket validation failed: " + e).initCause(e);
|
||||
}
|
||||
}
|
||||
logger.info("Login succeeded.");
|
||||
} else {
|
||||
logger.info("Login failed because callback handler did not provide CAS ticket.");
|
||||
throw new LoginException("Callback handler did not provide CAS ticket.");
|
||||
}
|
||||
result = true;
|
||||
} finally {
|
||||
postLogin(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean abort() throws LoginException {
|
||||
if (this.ticket != null) {
|
||||
this.ticket = null;
|
||||
}
|
||||
if (this.assertion != null) {
|
||||
this.assertion = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations to perform before doing commit.
|
||||
*
|
||||
* @return true if you'd like commit to continue, false otherwise.
|
||||
*/
|
||||
protected boolean preCommit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This occurs after commit is processed.
|
||||
*
|
||||
* @param result the result from the login attempt.
|
||||
*/
|
||||
protected void postCommit(final boolean result) {
|
||||
// template method
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean commit() throws LoginException {
|
||||
|
||||
if (!preCommit()) {
|
||||
return false;
|
||||
}
|
||||
boolean result = false;
|
||||
try {
|
||||
if (this.assertion != null) {
|
||||
if (this.ticket != null) {
|
||||
this.subject.getPrivateCredentials().add(this.ticket);
|
||||
} else {
|
||||
throw new LoginException("Ticket credential not found.");
|
||||
}
|
||||
|
||||
final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(),
|
||||
this.assertion);
|
||||
this.subject.getPrincipals().add(casPrincipal);
|
||||
|
||||
// Add group containing principal as sole member
|
||||
// Supports JBoss JAAS use case
|
||||
final Group principalGroup = new SimpleGroup(this.principalGroupName);
|
||||
principalGroup.addMember(casPrincipal);
|
||||
this.subject.getPrincipals().add(principalGroup);
|
||||
|
||||
// Add group principal containing role data
|
||||
final Group roleGroup = new SimpleGroup(this.roleGroupName);
|
||||
|
||||
for (final String defaultRole : defaultRoles) {
|
||||
roleGroup.addMember(new SimplePrincipal(defaultRole));
|
||||
}
|
||||
|
||||
final Map<String, Object> attributes = this.assertion.getPrincipal().getAttributes();
|
||||
for (final String key : attributes.keySet()) {
|
||||
if (this.roleAttributeNames.contains(key)) {
|
||||
// Attribute value is Object if singular or Collection if plural
|
||||
final Object value = attributes.get(key);
|
||||
if (value instanceof Collection<?>) {
|
||||
for (final Object o : (Collection<?>) value) {
|
||||
roleGroup.addMember(new SimplePrincipal(o.toString()));
|
||||
}
|
||||
} else {
|
||||
roleGroup.addMember(new SimplePrincipal(value.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.subject.getPrincipals().add(roleGroup);
|
||||
|
||||
// Place principal name in shared state for downstream JAAS modules (module chaining use case)
|
||||
this.sharedState.put(LOGIN_NAME, assertion.getPrincipal().getName());
|
||||
|
||||
logger.debug("Created JAAS subject with principals: {}", subject.getPrincipals());
|
||||
|
||||
if (this.cacheAssertions) {
|
||||
logger.debug("Caching assertion for principal {}", this.assertion.getPrincipal());
|
||||
ASSERTION_CACHE.put(this.ticket, this.assertion);
|
||||
}
|
||||
} else {
|
||||
// Login must have failed if there is no assertion defined
|
||||
// Need to clean up state
|
||||
if (this.ticket != null) {
|
||||
this.ticket = null;
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
} finally {
|
||||
postCommit(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean logout() throws LoginException {
|
||||
logger.debug("Performing logout.");
|
||||
|
||||
if (!preLogout()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove cache entry if assertion caching is enabled
|
||||
if (this.cacheAssertions) {
|
||||
for (final TicketCredential ticket : this.subject.getPrivateCredentials(TicketCredential.class)) {
|
||||
logger.debug("Removing cached assertion for {}", ticket);
|
||||
ASSERTION_CACHE.remove(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all CAS principals
|
||||
removePrincipalsOfType(AssertionPrincipal.class);
|
||||
removePrincipalsOfType(SimplePrincipal.class);
|
||||
removePrincipalsOfType(SimpleGroup.class);
|
||||
|
||||
// Remove all CAS credentials
|
||||
removeCredentialsOfType(TicketCredential.class);
|
||||
|
||||
logger.info("Logout succeeded.");
|
||||
|
||||
postLogout();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Happens before logout occurs.
|
||||
*
|
||||
* @return true if we should continue, false otherwise.
|
||||
*/
|
||||
protected boolean preLogout() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Happens after logout.
|
||||
*/
|
||||
protected void postLogout() {
|
||||
// template method
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TicketValidator} instance from a class name and map of property name/value pairs.
|
||||
* @param className Fully-qualified name of {@link TicketValidator} concrete class.
|
||||
* @param propertyMap Map of property name/value pairs to set on validator instance.
|
||||
* @return Ticket validator with properties set.
|
||||
*/
|
||||
private TicketValidator createTicketValidator(final String className, final Map<String, ?> propertyMap) {
|
||||
CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"),
|
||||
"Required property casServerUrlPrefix not found.");
|
||||
|
||||
final Class<TicketValidator> validatorClass = ReflectUtils.loadClass(className);
|
||||
final TicketValidator validator = ReflectUtils.newInstance(validatorClass,
|
||||
propertyMap.get("casServerUrlPrefix"));
|
||||
|
||||
try {
|
||||
final BeanInfo info = Introspector.getBeanInfo(validatorClass);
|
||||
|
||||
for (final String property : propertyMap.keySet()) {
|
||||
if (!"casServerUrlPrefix".equals(property)) {
|
||||
logger.debug("Attempting to set TicketValidator property {}", property);
|
||||
final String value = (String) propertyMap.get(property);
|
||||
final PropertyDescriptor pd = ReflectUtils.getPropertyDescriptor(info, property);
|
||||
if (pd != null) {
|
||||
ReflectUtils.setProperty(property, convertIfNecessary(pd, value), validator, info);
|
||||
logger.debug("Set {} = {}", property, value);
|
||||
} else {
|
||||
logger.warn("Cannot find property {} on {}", property, className);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Error getting bean info for " + validatorClass, e);
|
||||
}
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to do simple type conversion from a string value to the type expected
|
||||
* by the given property.
|
||||
*
|
||||
* Currently only conversion to int, long, and boolean are supported.
|
||||
*
|
||||
* @param pd Property descriptor of target property to set.
|
||||
* @param value Property value as a string.
|
||||
* @return Value converted to type expected by property if a conversion strategy exists.
|
||||
*/
|
||||
private static Object convertIfNecessary(final PropertyDescriptor pd, final String value) {
|
||||
if (String.class.equals(pd.getPropertyType())) {
|
||||
return value;
|
||||
} else if (boolean.class.equals(pd.getPropertyType())) {
|
||||
return Boolean.valueOf(value);
|
||||
} else if (int.class.equals(pd.getPropertyType())) {
|
||||
return new Integer(value);
|
||||
} else if (long.class.equals(pd.getPropertyType())) {
|
||||
return new Long(value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("No conversion strategy exists for property " + pd.getName()
|
||||
+ " of type " + pd.getPropertyType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all principals of the given type from the JAAS subject.
|
||||
* @param clazz Type of principal to remove.
|
||||
*/
|
||||
private void removePrincipalsOfType(final Class<? extends Principal> clazz) {
|
||||
this.subject.getPrincipals().removeAll(this.subject.getPrincipals(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all credentials of the given type from the JAAS subject.
|
||||
* @param clazz Type of principal to remove.
|
||||
*/
|
||||
private void removeCredentialsOfType(final Class<? extends Principal> clazz) {
|
||||
this.subject.getPrivateCredentials().removeAll(this.subject.getPrivateCredentials(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes expired entries from the assertion cache.
|
||||
*/
|
||||
private void cleanCache() {
|
||||
logger.debug("Cleaning assertion cache of size {}", ASSERTION_CACHE.size());
|
||||
final Iterator<Map.Entry<TicketCredential, Assertion>> iter = ASSERTION_CACHE.entrySet().iterator();
|
||||
final Calendar cutoff = Calendar.getInstance();
|
||||
cutoff.setTimeInMillis(System.currentTimeMillis() - this.cacheTimeoutUnit.toMillis(this.cacheTimeout));
|
||||
while (iter.hasNext()) {
|
||||
final Assertion assertion = iter.next().getValue();
|
||||
final Calendar created = Calendar.getInstance();
|
||||
created.setTime(assertion.getValidFromDate());
|
||||
if (created.before(cutoff)) {
|
||||
logger.debug("Removing expired assertion for principal {}", assertion.getPrincipal());
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.security.auth.callback.*;
|
||||
|
||||
/**
|
||||
* Callback handler that provides the CAS service and ticket to a
|
||||
* {@link NameCallback} and {@link PasswordCallback} respectively,
|
||||
* which meets the requirements of the {@link CasLoginModule} JAAS module.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class ServiceAndTicketCallbackHandler implements CallbackHandler {
|
||||
|
||||
/** CAS service URL */
|
||||
private final String service;
|
||||
|
||||
/** CAS service ticket */
|
||||
private final String ticket;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given service and ticket.
|
||||
*
|
||||
* @param service CAS service URL.
|
||||
* @param ticket CAS service ticket.
|
||||
*/
|
||||
public ServiceAndTicketCallbackHandler(final String service, final String ticket) {
|
||||
this.service = service;
|
||||
this.ticket = ticket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (final Callback callback : callbacks) {
|
||||
if (callback instanceof NameCallback) {
|
||||
((NameCallback) callback).setName(this.service);
|
||||
} else if (callback instanceof PasswordCallback) {
|
||||
((PasswordCallback) callback).setPassword(this.ticket.toCharArray());
|
||||
} else {
|
||||
throw new UnsupportedCallbackException(callback, "Callback not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Servlet filter performs a programmatic JAAS login using the Servlet 3.0 HttpServletRequest#login() facility.
|
||||
* This component should be compatible with any servlet container that supports the Servlet 3.0/JEE6 specification.
|
||||
* <p>
|
||||
* The filter executes when it receives a CAS ticket and expects the
|
||||
* {@link CasLoginModule} JAAS module to perform the CAS
|
||||
* ticket validation in order to produce an {@link org.jasig.cas.client.jaas.AssertionPrincipal} from which
|
||||
* the CAS assertion is obtained and inserted into the session to enable SSO.
|
||||
* <p>
|
||||
* If a <code>service</code> init-param is specified for this filter, it supersedes
|
||||
* the service defined for the {@link CasLoginModule}.
|
||||
*
|
||||
* @author Daniel Fisher
|
||||
* @author Marvin S. Addison
|
||||
* @since 3.3
|
||||
*/
|
||||
public final class Servlet3AuthenticationFilter extends AbstractCasFilter {
|
||||
|
||||
public Servlet3AuthenticationFilter() {
|
||||
super(Protocol.CAS2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
final HttpSession session = request.getSession();
|
||||
final String ticket = CommonUtils.safeGetParameter(request, getProtocol().getArtifactParameterName());
|
||||
|
||||
if (session != null && session.getAttribute(CONST_CAS_ASSERTION) == null && ticket != null) {
|
||||
try {
|
||||
final String service = constructServiceUrl(request, response);
|
||||
logger.debug("Attempting CAS ticket validation with service={} and ticket={}", service, ticket);
|
||||
request.login(service, ticket);
|
||||
if (request.getUserPrincipal() instanceof AssertionPrincipal) {
|
||||
final AssertionPrincipal principal = (AssertionPrincipal) request.getUserPrincipal();
|
||||
logger.debug("Installing CAS assertion into session.");
|
||||
request.getSession().setAttribute(CONST_CAS_ASSERTION, principal.getAssertion());
|
||||
} else {
|
||||
logger.debug("Aborting -- principal is not of type AssertionPrincipal");
|
||||
throw new GeneralSecurityException("JAAS authentication did not produce CAS AssertionPrincipal.");
|
||||
}
|
||||
} catch (final ServletException e) {
|
||||
logger.debug("JAAS authentication failed.");
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
|
||||
} catch (final GeneralSecurityException e) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
|
||||
}
|
||||
} else if (session != null && request.getUserPrincipal() == null) {
|
||||
// There is evidence that in some cases the principal can disappear
|
||||
// in JBoss despite a valid session.
|
||||
// This block forces consistency between principal and assertion.
|
||||
logger.info("User principal not found. Removing CAS assertion from session to force re-authentication.");
|
||||
session.removeAttribute(CONST_CAS_ASSERTION);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Strongly-typed wrapper for a ticket credential.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.12
|
||||
*
|
||||
*/
|
||||
public final class TicketCredential implements Principal {
|
||||
|
||||
/** Hash code seed value */
|
||||
private static final int HASHCODE_SEED = 17;
|
||||
|
||||
/** Ticket ID string */
|
||||
private final String ticket;
|
||||
|
||||
/**
|
||||
* Creates a new instance that wraps the given ticket.
|
||||
* @param ticket Ticket identifier string.
|
||||
*/
|
||||
public TicketCredential(final String ticket) {
|
||||
this.ticket = ticket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.ticket;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.ticket;
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
final TicketCredential that = (TicketCredential) o;
|
||||
|
||||
if (ticket != null ? !ticket.equals(that.ticket) : that.ticket != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = HASHCODE_SEED;
|
||||
hash = hash * 31 + (ticket == null ? 0 : ticket.hashCode());
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESedeKeySpec;
|
||||
|
||||
/**
|
||||
* Provides encryption capabilities. Not entirely safe to configure since we have no way of controlling the
|
||||
* key and cipher being set.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public abstract class AbstractEncryptedProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage {
|
||||
|
||||
private Key key;
|
||||
|
||||
private String cipherAlgorithm = ConfigurationKeys.CIPHER_ALGORITHM.getDefaultValue();
|
||||
|
||||
public final void setSecretKey(final String key) throws NoSuchAlgorithmException, InvalidKeyException,
|
||||
InvalidKeySpecException {
|
||||
this.key = SecretKeyFactory.getInstance(this.cipherAlgorithm).generateSecret(new DESedeKeySpec(key.getBytes()));
|
||||
}
|
||||
|
||||
public final void setSecretKey(final Key key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: you MUST call this method before calling setSecretKey if you're not using the default algorithm. You've been warned.
|
||||
*
|
||||
* @param cipherAlgorithm the cipher algorithm.
|
||||
*/
|
||||
public final void setCipherAlgorithm(final String cipherAlgorithm) {
|
||||
this.cipherAlgorithm = cipherAlgorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void save(final String proxyGrantingTicketIou, final String proxyGrantingTicket) {
|
||||
saveInternal(proxyGrantingTicketIou, encrypt(proxyGrantingTicket));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String retrieve(final String proxyGrantingTicketIou) {
|
||||
return decrypt(retrieveInternal(proxyGrantingTicketIou));
|
||||
}
|
||||
|
||||
protected abstract void saveInternal(String proxyGrantingTicketIou, String proxyGrantingTicket);
|
||||
|
||||
protected abstract String retrieveInternal(String proxyGrantingTicketIou);
|
||||
|
||||
private String encrypt(final String value) {
|
||||
if (this.key == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(this.cipherAlgorithm);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, this.key);
|
||||
return new String(cipher.doFinal(value.getBytes()));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String decrypt(final String value) {
|
||||
if (this.key == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(this.cipherAlgorithm);
|
||||
cipher.init(Cipher.DECRYPT_MODE, this.key);
|
||||
return new String(cipher.doFinal(value.getBytes()));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +1,110 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implementation of a ProxyRetriever that follows the CAS 2.0 specification.
|
||||
* For more information on the CAS 2.0 specification, please see the <a
|
||||
* href="http://www.ja-sig.org/products/cas/overview/protocol/index.html">specification
|
||||
* href="http://www.jasig.org/cas/protocol">specification
|
||||
* document</a>.
|
||||
* <p/>
|
||||
* In general, this class will make a call to the CAS server with some specified
|
||||
* parameters and receive an XML response to parse.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class Cas20ProxyRetriever implements ProxyRetriever {
|
||||
|
||||
/**
|
||||
* Instance of Commons Logging.
|
||||
*/
|
||||
private final Log log = LogFactory.getLog(this.getClass());
|
||||
/** Unique Id for serialization. */
|
||||
private static final long serialVersionUID = 560409469568911792L;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Cas20ProxyRetriever.class);
|
||||
|
||||
/**
|
||||
* Url to CAS server.
|
||||
*/
|
||||
private final String casServerUrl;
|
||||
|
||||
private final String encoding;
|
||||
|
||||
/** Url connection factory to use when communicating with the server **/
|
||||
private final HttpURLConnectionFactory urlConnectionFactory;
|
||||
|
||||
@Deprecated
|
||||
public Cas20ProxyRetriever(final String casServerUrl, final String encoding) {
|
||||
this(casServerUrl, encoding, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Constructor.
|
||||
*
|
||||
* @param casServerUrl the URL to the CAS server (i.e. http://localhost/cas/)
|
||||
* @param encoding the encoding to use.
|
||||
* @param urlFactory url connection factory use when retrieving proxy responses from the server
|
||||
*/
|
||||
public Cas20ProxyRetriever(final String casServerUrl) {
|
||||
CommonUtils.assertNotNull(casServerUrl,
|
||||
"casServerUrl cannot be null.");
|
||||
public Cas20ProxyRetriever(final String casServerUrl, final String encoding,
|
||||
final HttpURLConnectionFactory urlFactory) {
|
||||
CommonUtils.assertNotNull(casServerUrl, "casServerUrl cannot be null.");
|
||||
this.casServerUrl = casServerUrl;
|
||||
this.encoding = encoding;
|
||||
this.urlConnectionFactory = urlFactory;
|
||||
}
|
||||
|
||||
public String getProxyTicketIdFor(final String proxyGrantingTicketId,
|
||||
final String targetService) {
|
||||
@Override
|
||||
public String getProxyTicketIdFor(final String proxyGrantingTicketId, final String targetService) {
|
||||
CommonUtils.assertNotNull(proxyGrantingTicketId, "proxyGrantingTicketId cannot be null.");
|
||||
CommonUtils.assertNotNull(targetService, "targetService cannot be null.");
|
||||
|
||||
final String url = constructUrl(proxyGrantingTicketId, targetService);
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
final URL constructedUrl = new URL(url);
|
||||
conn = (HttpURLConnection) constructedUrl.openConnection();
|
||||
final URL url = constructUrl(proxyGrantingTicketId, targetService);
|
||||
final String response;
|
||||
|
||||
final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
String line;
|
||||
final StringBuffer stringBuffer = new StringBuffer(255);
|
||||
final String response;
|
||||
|
||||
synchronized (stringBuffer) {
|
||||
while ((line = in.readLine()) != null) {
|
||||
stringBuffer.append(line);
|
||||
}
|
||||
response = stringBuffer.toString();
|
||||
}
|
||||
|
||||
final String error = XmlUtils.getTextForElement(response,
|
||||
"proxyFailure");
|
||||
|
||||
if (CommonUtils.isNotEmpty(error)) {
|
||||
log.debug(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return XmlUtils.getTextForElement(response, "proxyTicket");
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
if (this.urlConnectionFactory != null) {
|
||||
response = CommonUtils.getResponseFromServer(url, this.urlConnectionFactory, this.encoding);
|
||||
} else {
|
||||
response = CommonUtils.getResponseFromServer(url, this.encoding);
|
||||
}
|
||||
final String error = XmlUtils.getTextForElement(response, "proxyFailure");
|
||||
|
||||
if (CommonUtils.isNotEmpty(error)) {
|
||||
logger.debug(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
final String ticket = XmlUtils.getTextForElement(response, "proxyTicket");
|
||||
logger.debug("Got proxy ticket {}", ticket);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
private String constructUrl(final String proxyGrantingTicketId,
|
||||
final String targetService) {
|
||||
private URL constructUrl(final String proxyGrantingTicketId, final String targetService) {
|
||||
try {
|
||||
return this.casServerUrl + "proxy" + "?pgt="
|
||||
+ proxyGrantingTicketId + "&targetService="
|
||||
+ URLEncoder.encode(targetService, "UTF-8");
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
return new URL(this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt="
|
||||
+ proxyGrantingTicketId + "&targetService=" + URLEncoder.encode(targetService, "UTF-8"));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* A {@link TimerTask} implementation which performs the
|
||||
* actual 'cleaning' by calling {@link ProxyGrantingTicketStorage#cleanUp()}.
|
||||
* <p>
|
||||
* By default, the {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter} configures
|
||||
* a task that cleans up the {@link org.jasig.cas.client.proxy.ProxyGrantingTicketStorage} associated with it.
|
||||
*
|
||||
* @author Brad Cupit (brad [at] lsu {dot} edu)
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.6
|
||||
*/
|
||||
public final class CleanUpTimerTask extends TimerTask {
|
||||
|
||||
private final ProxyGrantingTicketStorage proxyGrantingTicketStorage;
|
||||
|
||||
public CleanUpTimerTask(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
|
||||
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.proxyGrantingTicketStorage.cleanUp();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
|
|
@ -10,7 +23,6 @@ package org.jasig.cas.client.proxy;
|
|||
* them to a specific ProxyGrantingTicketIou.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ProxyGrantingTicketStorage {
|
||||
|
|
@ -33,4 +45,10 @@ public interface ProxyGrantingTicketStorage {
|
|||
* @return the ProxyGrantingTicket Id or null if it can't be found
|
||||
*/
|
||||
public String retrieve(String proxyGrantingTicketIou);
|
||||
|
||||
/**
|
||||
* Called on a regular basis by an external timer,
|
||||
* giving implementations a chance to remove stale data.
|
||||
*/
|
||||
public void cleanUp();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,44 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ProxyGrantingTicketStorage} that is backed by a
|
||||
* HashMap that keeps a ProxyGrantingTicket for a specified amount of time.
|
||||
* <p/>
|
||||
* A cleanup thread is run periodically to clean out the HashMap.
|
||||
* <p>
|
||||
* {@link ProxyGrantingTicketStorage#cleanUp()} must be called on a regular basis to
|
||||
* keep the HashMap from growing indefinitely.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @author Brad Cupit (brad [at] lsu {dot} edu)
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class ProxyGrantingTicketStorageImpl implements
|
||||
ProxyGrantingTicketStorage {
|
||||
public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Default timeout in milliseconds.
|
||||
|
|
@ -32,7 +48,15 @@ public final class ProxyGrantingTicketStorageImpl implements
|
|||
/**
|
||||
* Map that stores the PGTIOU to PGT mappings.
|
||||
*/
|
||||
private final Map cache = new HashMap();
|
||||
private final ConcurrentMap<String, ProxyGrantingTicketHolder> cache = new ConcurrentHashMap<String, ProxyGrantingTicketHolder>();
|
||||
|
||||
/**
|
||||
* time, in milliseconds, before a {@link ProxyGrantingTicketHolder}
|
||||
* is considered expired and ready for removal.
|
||||
*
|
||||
* @see ProxyGrantingTicketStorageImpl#DEFAULT_TIMEOUT
|
||||
*/
|
||||
private final long timeout;
|
||||
|
||||
/**
|
||||
* Constructor set the timeout to the default value.
|
||||
|
|
@ -48,38 +72,55 @@ public final class ProxyGrantingTicketStorageImpl implements
|
|||
* @param timeout the time to hold on to the ProxyGrantingTicket
|
||||
*/
|
||||
public ProxyGrantingTicketStorageImpl(final long timeout) {
|
||||
final Thread thread = new ProxyGrantingTicketCleanupThread(
|
||||
timeout, this.cache);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: you can only retrieve a ProxyGrantingTicket once with this method.
|
||||
* Its removed after retrieval.
|
||||
*/
|
||||
@Override
|
||||
public String retrieve(final String proxyGrantingTicketIou) {
|
||||
final ProxyGrantingTicketHolder holder = (ProxyGrantingTicketHolder) this.cache
|
||||
.get(proxyGrantingTicketIou);
|
||||
|
||||
if (holder == null) {
|
||||
if (CommonUtils.isBlank(proxyGrantingTicketIou)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.cache.remove(holder);
|
||||
final ProxyGrantingTicketHolder holder = this.cache.get(proxyGrantingTicketIou);
|
||||
|
||||
if (holder == null) {
|
||||
logger.info("No Proxy Ticket found for [{}].", proxyGrantingTicketIou);
|
||||
return null;
|
||||
}
|
||||
|
||||
this.cache.remove(proxyGrantingTicketIou);
|
||||
|
||||
logger.debug("Returned ProxyGrantingTicket of [{}]", holder.getProxyGrantingTicket());
|
||||
return holder.getProxyGrantingTicket();
|
||||
}
|
||||
|
||||
public void save(final String proxyGrantingTicketIou,
|
||||
final String proxyGrantingTicket) {
|
||||
final ProxyGrantingTicketHolder holder = new ProxyGrantingTicketHolder(
|
||||
proxyGrantingTicket);
|
||||
@Override
|
||||
public void save(final String proxyGrantingTicketIou, final String proxyGrantingTicket) {
|
||||
final ProxyGrantingTicketHolder holder = new ProxyGrantingTicketHolder(proxyGrantingTicket);
|
||||
|
||||
logger.debug("Saving ProxyGrantingTicketIOU and ProxyGrantingTicket combo: [{}, {}]", proxyGrantingTicketIou,
|
||||
proxyGrantingTicket);
|
||||
this.cache.put(proxyGrantingTicketIou, holder);
|
||||
}
|
||||
|
||||
private final class ProxyGrantingTicketHolder {
|
||||
/**
|
||||
* Cleans up old, expired proxy tickets. This method must be
|
||||
* called regularly via an external thread or timer.
|
||||
*/
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
for (final Map.Entry<String, ProxyGrantingTicketHolder> holder : this.cache.entrySet()) {
|
||||
if (holder.getValue().isExpired(this.timeout)) {
|
||||
this.cache.remove(holder.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProxyGrantingTicketHolder {
|
||||
|
||||
private final String proxyGrantingTicket;
|
||||
|
||||
|
|
@ -98,48 +139,4 @@ public final class ProxyGrantingTicketStorageImpl implements
|
|||
return System.currentTimeMillis() - this.timeInserted > timeout;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ProxyGrantingTicketCleanupThread extends Thread {
|
||||
|
||||
private final long timeout;
|
||||
|
||||
private final Map cache;
|
||||
|
||||
public ProxyGrantingTicketCleanupThread(final long timeout,
|
||||
final Map cache) {
|
||||
this.timeout = timeout;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(this.timeout);
|
||||
} catch (final InterruptedException e) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
final List itemsToRemove = new ArrayList();
|
||||
|
||||
synchronized (this.cache) {
|
||||
for (final Iterator iter = this.cache.keySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
final Object key = iter.next();
|
||||
final ProxyGrantingTicketHolder holder = (ProxyGrantingTicketHolder) this.cache
|
||||
.get(key);
|
||||
|
||||
if (holder.isExpired(this.timeout)) {
|
||||
itemsToRemove.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (final Iterator iter = itemsToRemove.iterator(); iter
|
||||
.hasNext();) {
|
||||
this.cache.remove(iter.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Interface to abstract the retrieval of a proxy ticket to make the
|
||||
* implementation a black box to the client.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ProxyRetriever {
|
||||
public interface ProxyRetriever extends Serializable {
|
||||
|
||||
/**
|
||||
* Retrieves a proxy ticket for a specific targetService.
|
||||
|
|
@ -22,6 +36,5 @@ public interface ProxyRetriever {
|
|||
* @param targetService the service we want to proxy.
|
||||
* @return the ProxyTicket Id if Granted, null otherwise.
|
||||
*/
|
||||
String getProxyTicketIdFor(String proxyGrantingTicketId,
|
||||
String targetService);
|
||||
String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,23 @@
|
|||
<!--
|
||||
|
||||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
<p>The proxy package includes a servlet to act as a proxy receptor,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,28 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* HashMap backed implementation of SessionMappingStorage.
|
||||
|
|
@ -18,38 +32,52 @@ import javax.servlet.http.HttpSession;
|
|||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public final class HashMapBackedSessionMappingStorage implements
|
||||
SessionMappingStorage {
|
||||
|
||||
public final class HashMapBackedSessionMappingStorage implements SessionMappingStorage {
|
||||
|
||||
/**
|
||||
* Maps the ID from the CAS server to the Session.
|
||||
*/
|
||||
private final Map MANAGED_SESSIONS = new HashMap();
|
||||
private final Map<String, HttpSession> MANAGED_SESSIONS = new HashMap<String, HttpSession>();
|
||||
|
||||
/**
|
||||
* Maps the Session ID to the key from the CAS Server.
|
||||
*/
|
||||
private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap();
|
||||
private final Map<String, String> ID_TO_SESSION_KEY_MAPPING = new HashMap<String, String>();
|
||||
|
||||
public void addSessionById(String mappingId, HttpSession session) {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public synchronized void addSessionById(final String mappingId, final HttpSession session) {
|
||||
ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);
|
||||
MANAGED_SESSIONS.put(mappingId, session);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void removeBySessionById(String sessionId) {
|
||||
final String key = (String) ID_TO_SESSION_KEY_MAPPING.get(sessionId);
|
||||
@Override
|
||||
public synchronized void removeBySessionById(final String sessionId) {
|
||||
logger.debug("Attempting to remove Session=[{}]", sessionId);
|
||||
|
||||
final String key = ID_TO_SESSION_KEY_MAPPING.get(sessionId);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (key != null) {
|
||||
logger.debug("Found mapping for session. Session Removed.");
|
||||
} else {
|
||||
logger.debug("No mapping for session found. Ignoring.");
|
||||
}
|
||||
}
|
||||
MANAGED_SESSIONS.remove(key);
|
||||
ID_TO_SESSION_KEY_MAPPING.remove(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpSession removeSessionByMappingId(String mappingId) {
|
||||
final HttpSession session = (HttpSession) MANAGED_SESSIONS.get(mappingId);
|
||||
@Override
|
||||
public synchronized HttpSession removeSessionByMappingId(final String mappingId) {
|
||||
final HttpSession session = MANAGED_SESSIONS.get(mappingId);
|
||||
|
||||
if (session != null) {
|
||||
removeBySessionById(session.getId());
|
||||
removeBySessionById(session.getId());
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
|
|
@ -16,26 +29,26 @@ import javax.servlet.http.HttpSession;
|
|||
*
|
||||
*/
|
||||
public interface SessionMappingStorage {
|
||||
|
||||
/**
|
||||
* Remove the HttpSession based on the mappingId.
|
||||
*
|
||||
* @param mappingId the id the session is keyed under.
|
||||
* @return the HttpSession if it exists.
|
||||
*/
|
||||
HttpSession removeSessionByMappingId(String mappingId);
|
||||
|
||||
/**
|
||||
* Remove a session by its Id.
|
||||
* @param sessionId the id of the session.
|
||||
*/
|
||||
void removeBySessionById(String sessionId);
|
||||
|
||||
/**
|
||||
* Add a session by its mapping Id.
|
||||
* @param mappingId the id to map the session to.
|
||||
* @param session the HttpSession.
|
||||
*/
|
||||
void addSessionById(String mappingId, HttpSession session);
|
||||
|
||||
/**
|
||||
* Remove the HttpSession based on the mappingId.
|
||||
*
|
||||
* @param mappingId the id the session is keyed under.
|
||||
* @return the HttpSession if it exists.
|
||||
*/
|
||||
HttpSession removeSessionByMappingId(String mappingId);
|
||||
|
||||
/**
|
||||
* Remove a session by its Id.
|
||||
* @param sessionId the id of the session.
|
||||
*/
|
||||
void removeBySessionById(String sessionId);
|
||||
|
||||
/**
|
||||
* Add a session by its mapping Id.
|
||||
* @param mappingId the id to map the session to.
|
||||
* @param session the HttpSession.
|
||||
*/
|
||||
void addSessionById(String mappingId, HttpSession session);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,32 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import org.jasig.cas.client.util.AbstractConfigurationFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.util.AbstractConfigurationFilter;
|
||||
|
||||
/**
|
||||
* Implements the Single Sign Out protocol. It handles registering the session and destroying the session.
|
||||
|
|
@ -27,66 +37,70 @@ import java.io.IOException;
|
|||
*/
|
||||
public final class SingleSignOutFilter extends AbstractConfigurationFilter {
|
||||
|
||||
/**
|
||||
* The name of the artifact parameter. This is used to capture the session identifier.
|
||||
*/
|
||||
private String artifactParameterName = "ticket";
|
||||
|
||||
private static SessionMappingStorage SESSION_MAPPING_STORAGE = new HashMapBackedSessionMappingStorage();
|
||||
private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();
|
||||
|
||||
private final AtomicBoolean handlerInitialized = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
|
||||
|
||||
init();
|
||||
super.init(filterConfig);
|
||||
if (!isIgnoreInitConfiguration()) {
|
||||
setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME));
|
||||
setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME));
|
||||
setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME));
|
||||
setLogoutCallbackPath(getString(ConfigurationKeys.LOGOUT_CALLBACK_PATH));
|
||||
HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST));
|
||||
HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS));
|
||||
}
|
||||
HANDLER.init();
|
||||
handlerInitialized.set(true);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(SESSION_MAPPING_STORAGE, "sessionMappingStorage cannote be null.");
|
||||
public void setArtifactParameterName(final String name) {
|
||||
HANDLER.setArtifactParameterName(name);
|
||||
}
|
||||
|
||||
public void setArtifactParameterName(final String artifactParameterName) {
|
||||
this.artifactParameterName = artifactParameterName;
|
||||
public void setLogoutParameterName(final String name) {
|
||||
HANDLER.setLogoutParameterName(name);
|
||||
}
|
||||
|
||||
public void setRelayStateParameterName(final String name) {
|
||||
HANDLER.setRelayStateParameterName(name);
|
||||
}
|
||||
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
public void setLogoutCallbackPath(final String logoutCallbackPath) {
|
||||
HANDLER.setLogoutCallbackPath(logoutCallbackPath);
|
||||
}
|
||||
|
||||
public void setSessionMappingStorage(final SessionMappingStorage storage) {
|
||||
HANDLER.setSessionMappingStorage(storage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
|
||||
if ("POST".equals(request.getMethod())) {
|
||||
final String logoutRequest = request.getParameter("logoutRequest");
|
||||
|
||||
if (CommonUtils.isNotBlank(logoutRequest)) {
|
||||
final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
|
||||
|
||||
if (CommonUtils.isNotBlank(sessionIdentifier)) {
|
||||
final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
|
||||
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final String artifact = request.getParameter(this.artifactParameterName);
|
||||
final HttpSession session = request.getSession();
|
||||
if (CommonUtils.isNotBlank(artifact)) {
|
||||
SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
|
||||
}
|
||||
/**
|
||||
* <p>Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.</p>
|
||||
* <p>Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.</p>
|
||||
*/
|
||||
if (!this.handlerInitialized.getAndSet(true)) {
|
||||
HANDLER.init();
|
||||
}
|
||||
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
public void setSessionMappingStorage(final SessionMappingStorage storage) {
|
||||
SESSION_MAPPING_STORAGE = storage;
|
||||
}
|
||||
|
||||
public static SessionMappingStorage getSessionMappingStorage() {
|
||||
return SESSION_MAPPING_STORAGE;
|
||||
if (HANDLER.process(request, response)) {
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected static SingleSignOutHandler getSingleSignOutHandler() {
|
||||
return HANDLER;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,333 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Performs CAS single sign-out operations in an API-agnostic fashion.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.12
|
||||
*
|
||||
*/
|
||||
public final class SingleSignOutHandler {
|
||||
|
||||
private final static int DECOMPRESSION_FACTOR = 10;
|
||||
|
||||
/** Logger instance */
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/** Mapping of token IDs and session IDs to HTTP sessions */
|
||||
private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
|
||||
|
||||
/** The name of the artifact parameter. This is used to capture the session identifier. */
|
||||
private String artifactParameterName = Protocol.CAS2.getArtifactParameterName();
|
||||
|
||||
/** Parameter name that stores logout request for SLO */
|
||||
private String logoutParameterName = ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue();
|
||||
|
||||
/** Parameter name that stores the state of the CAS server webflow for the callback */
|
||||
private String relayStateParameterName = ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue();
|
||||
|
||||
/** The logout callback path configured at the CAS server, if there is one */
|
||||
private String logoutCallbackPath;
|
||||
|
||||
private boolean artifactParameterOverPost = false;
|
||||
|
||||
private boolean eagerlyCreateSessions = true;
|
||||
|
||||
private List<String> safeParameters;
|
||||
|
||||
private final LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy();
|
||||
|
||||
public void setSessionMappingStorage(final SessionMappingStorage storage) {
|
||||
this.sessionMappingStorage = storage;
|
||||
}
|
||||
|
||||
public void setArtifactParameterOverPost(final boolean artifactParameterOverPost) {
|
||||
this.artifactParameterOverPost = artifactParameterOverPost;
|
||||
}
|
||||
|
||||
public SessionMappingStorage getSessionMappingStorage() {
|
||||
return this.sessionMappingStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of the authentication token parameter.
|
||||
*/
|
||||
public void setArtifactParameterName(final String name) {
|
||||
this.artifactParameterName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of parameter containing CAS logout request message for SLO.
|
||||
*/
|
||||
public void setLogoutParameterName(final String name) {
|
||||
this.logoutParameterName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param logoutCallbackPath The logout callback path configured at the CAS server.
|
||||
*/
|
||||
public void setLogoutCallbackPath(final String logoutCallbackPath) {
|
||||
this.logoutCallbackPath = logoutCallbackPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of parameter containing the state of the CAS server webflow.
|
||||
*/
|
||||
public void setRelayStateParameterName(final String name) {
|
||||
this.relayStateParameterName = name;
|
||||
}
|
||||
|
||||
public void setEagerlyCreateSessions(final boolean eagerlyCreateSessions) {
|
||||
this.eagerlyCreateSessions = eagerlyCreateSessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the component for use.
|
||||
*/
|
||||
public synchronized void init() {
|
||||
if (this.safeParameters == null) {
|
||||
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannot be null.");
|
||||
CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
|
||||
|
||||
if (this.artifactParameterOverPost) {
|
||||
this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
|
||||
} else {
|
||||
this.safeParameters = Collections.singletonList(this.logoutParameterName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given request contains an authentication token.
|
||||
*
|
||||
* @param request HTTP reqest.
|
||||
*
|
||||
* @return True if request contains authentication token, false otherwise.
|
||||
*/
|
||||
private boolean isTokenRequest(final HttpServletRequest request) {
|
||||
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName,
|
||||
this.safeParameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given request is a CAS logout request.
|
||||
*
|
||||
* @param request HTTP request.
|
||||
*
|
||||
* @return True if request is logout request, false otherwise.
|
||||
*/
|
||||
private boolean isLogoutRequest(final HttpServletRequest request) {
|
||||
if ("POST".equalsIgnoreCase(request.getMethod())) {
|
||||
return !isMultipartRequest(request)
|
||||
&& pathEligibleForLogout(request)
|
||||
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName,
|
||||
this.safeParameters));
|
||||
}
|
||||
|
||||
if ("GET".equalsIgnoreCase(request.getMethod())) {
|
||||
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean pathEligibleForLogout(final HttpServletRequest request) {
|
||||
return logoutCallbackPath == null || logoutCallbackPath.equals(getPath(request));
|
||||
}
|
||||
|
||||
private String getPath(final HttpServletRequest request) {
|
||||
return request.getServletPath() + CommonUtils.nullToEmpty(request.getPathInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a request regarding the SLO process: record the session or destroy it.
|
||||
*
|
||||
* @param request the incoming HTTP request.
|
||||
* @param response the HTTP response.
|
||||
* @return if the request should continue to be processed.
|
||||
*/
|
||||
public boolean process(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
if (isTokenRequest(request)) {
|
||||
logger.trace("Received a token request");
|
||||
recordSession(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLogoutRequest(request)) {
|
||||
logger.trace("Received a logout request");
|
||||
destroySession(request);
|
||||
return false;
|
||||
}
|
||||
logger.trace("Ignoring URI for logout: {}", request.getRequestURI());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a token request with the current HTTP session by recording the mapping
|
||||
* in the the configured {@link SessionMappingStorage} container.
|
||||
*
|
||||
* @param request HTTP request containing an authentication token.
|
||||
*/
|
||||
private void recordSession(final HttpServletRequest request) {
|
||||
final HttpSession session = request.getSession(this.eagerlyCreateSessions);
|
||||
|
||||
if (session == null) {
|
||||
logger.debug("No session currently exists (and none created). Cannot record session information for single sign out.");
|
||||
return;
|
||||
}
|
||||
|
||||
final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters);
|
||||
logger.debug("Recording session for token {}", token);
|
||||
|
||||
try {
|
||||
this.sessionMappingStorage.removeBySessionById(session.getId());
|
||||
} catch (final Exception e) {
|
||||
// ignore if the session is already marked as invalid. Nothing we can do!
|
||||
}
|
||||
sessionMappingStorage.addSessionById(token, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncompress a logout message (base64 + deflate).
|
||||
*
|
||||
* @param originalMessage the original logout message.
|
||||
* @return the uncompressed logout message.
|
||||
*/
|
||||
private String uncompressLogoutMessage(final String originalMessage) {
|
||||
final byte[] binaryMessage = DatatypeConverter.parseBase64Binary(originalMessage);
|
||||
|
||||
Inflater decompresser = null;
|
||||
try {
|
||||
// decompress the bytes
|
||||
decompresser = new Inflater();
|
||||
decompresser.setInput(binaryMessage);
|
||||
final byte[] result = new byte[binaryMessage.length * DECOMPRESSION_FACTOR];
|
||||
|
||||
final int resultLength = decompresser.inflate(result);
|
||||
|
||||
// decode the bytes into a String
|
||||
return new String(result, 0, resultLength, "UTF-8");
|
||||
} catch (final Exception e) {
|
||||
logger.error("Unable to decompress logout message", e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (decompresser != null) {
|
||||
decompresser.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the current HTTP session for the given CAS logout request.
|
||||
*
|
||||
* @param request HTTP request containing a CAS logout message.
|
||||
*/
|
||||
private void destroySession(final HttpServletRequest request) {
|
||||
String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters);
|
||||
if (CommonUtils.isBlank(logoutMessage)) {
|
||||
logger.error("Could not locate logout message of the request from {}", this.logoutParameterName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!logoutMessage.contains("SessionIndex")) {
|
||||
logoutMessage = uncompressLogoutMessage(logoutMessage);
|
||||
}
|
||||
|
||||
logger.trace("Logout request:\n{}", logoutMessage);
|
||||
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
|
||||
if (CommonUtils.isNotBlank(token)) {
|
||||
final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token);
|
||||
|
||||
if (session != null) {
|
||||
final String sessionID = session.getId();
|
||||
logger.debug("Invalidating session [{}] for token [{}]", sessionID, token);
|
||||
|
||||
try {
|
||||
session.invalidate();
|
||||
} catch (final IllegalStateException e) {
|
||||
logger.debug("Error invalidating session.", e);
|
||||
}
|
||||
this.logoutStrategy.logout(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMultipartRequest(final HttpServletRequest request) {
|
||||
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
|
||||
}
|
||||
|
||||
private static boolean isServlet30() {
|
||||
try {
|
||||
return HttpServletRequest.class.getMethod("logout") != null;
|
||||
} catch (final NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstracts the ways we can force logout with the Servlet spec.
|
||||
*/
|
||||
private interface LogoutStrategy {
|
||||
|
||||
void logout(HttpServletRequest request);
|
||||
}
|
||||
|
||||
private class Servlet25LogoutStrategy implements LogoutStrategy {
|
||||
|
||||
@Override
|
||||
public void logout(final HttpServletRequest request) {
|
||||
// nothing additional to do here
|
||||
}
|
||||
}
|
||||
|
||||
private class Servlet30LogoutStrategy implements LogoutStrategy {
|
||||
|
||||
@Override
|
||||
public void logout(final HttpServletRequest request) {
|
||||
try {
|
||||
request.logout();
|
||||
} catch (final ServletException e) {
|
||||
logger.debug("Error performing request.logout.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
|
|
@ -21,18 +34,20 @@ import javax.servlet.http.HttpSessionListener;
|
|||
*/
|
||||
public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
|
||||
|
||||
private SessionMappingStorage SESSION_MAPPING_STORAGE;
|
||||
|
||||
private SessionMappingStorage sessionMappingStorage;
|
||||
|
||||
@Override
|
||||
public void sessionCreated(final HttpSessionEvent event) {
|
||||
// nothing to do at the moment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(final HttpSessionEvent event) {
|
||||
if (SESSION_MAPPING_STORAGE == null) {
|
||||
SESSION_MAPPING_STORAGE = getSessionMappingStorage();
|
||||
}
|
||||
if (sessionMappingStorage == null) {
|
||||
sessionMappingStorage = getSessionMappingStorage();
|
||||
}
|
||||
final HttpSession session = event.getSession();
|
||||
SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
|
||||
sessionMappingStorage.removeBySessionById(session.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,6 +57,6 @@ public final class SingleSignOutHttpSessionListener implements HttpSessionListen
|
|||
* @return the SessionMappingStorage
|
||||
*/
|
||||
protected static SessionMappingStorage getSessionMappingStorage() {
|
||||
return SingleSignOutFilter.getSessionMappingStorage();
|
||||
return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Hostname verifier that performs no host name verification for an SSL peer
|
||||
* such that all hosts are allowed.
|
||||
*
|
||||
* @author Marvin Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.10
|
||||
*/
|
||||
public final class AnyHostnameVerifier implements HostnameVerifier {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* A factory to prepare and configure {@link java.net.URLConnection} instances.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3
|
||||
*/
|
||||
public interface HttpURLConnectionFactory extends Serializable {
|
||||
|
||||
/**
|
||||
* Receives a {@link URLConnection} instance typically as a result of a {@link URL}
|
||||
* opening a connection to a remote resource. The received url connection is then
|
||||
* configured and prepared appropriately depending on its type and is then returned to the caller
|
||||
* to accommodate method chaining.
|
||||
*
|
||||
* @param url The url connection that needs to be configured
|
||||
* @return The configured {@link HttpURLConnection} instance
|
||||
*
|
||||
* @see {@link HttpsURLConnectionFactory}
|
||||
*/
|
||||
HttpURLConnection buildHttpURLConnection(final URLConnection url);
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLConnection;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Properties;
|
||||
import javax.net.ssl.*;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link HttpURLConnectionFactory} whose responsible to configure
|
||||
* the underlying <i>https</i> connection, if needed, with a given hostname and SSL socket factory based on the
|
||||
* configuration provided.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3
|
||||
* @see #setHostnameVerifier(HostnameVerifier)
|
||||
* @see #setSSLConfiguration(Properties)
|
||||
*/
|
||||
public final class HttpsURLConnectionFactory implements HttpURLConnectionFactory {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpsURLConnectionFactory.class);
|
||||
|
||||
/**
|
||||
* Hostname verifier used when making an SSL request to the CAS server.
|
||||
* Defaults to {@link HttpsURLConnection#getDefaultHostnameVerifier()}
|
||||
*/
|
||||
private HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
|
||||
/**
|
||||
* Properties file that can contains key/trust info for Client Side Certificates
|
||||
*/
|
||||
private Properties sslConfiguration = new Properties();
|
||||
|
||||
public HttpsURLConnectionFactory() {
|
||||
}
|
||||
|
||||
public HttpsURLConnectionFactory(final HostnameVerifier verifier, final Properties config) {
|
||||
setHostnameVerifier(verifier);
|
||||
setSSLConfiguration(config);
|
||||
}
|
||||
|
||||
public final void setSSLConfiguration(final Properties config) {
|
||||
this.sslConfiguration = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host name verifier for the https connection received.
|
||||
*
|
||||
* @see AnyHostnameVerifier
|
||||
* @see RegexHostnameVerifier
|
||||
* @see WhitelistHostnameVerifier
|
||||
*/
|
||||
public final void setHostnameVerifier(final HostnameVerifier verifier) {
|
||||
this.hostnameVerifier = verifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpURLConnection buildHttpURLConnection(final URLConnection url) {
|
||||
return this.configureHttpsConnectionIfNeeded(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the connection with specific settings for secure http connections
|
||||
* If the connection instance is not a {@link HttpsURLConnection},
|
||||
* no additional changes will be made and the connection itself is simply returned.
|
||||
*
|
||||
* @param conn the http connection
|
||||
*/
|
||||
private HttpURLConnection configureHttpsConnectionIfNeeded(final URLConnection conn) {
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
final HttpsURLConnection httpsConnection = (HttpsURLConnection) conn;
|
||||
final SSLSocketFactory socketFactory = this.createSSLSocketFactory();
|
||||
if (socketFactory != null) {
|
||||
httpsConnection.setSSLSocketFactory(socketFactory);
|
||||
}
|
||||
|
||||
if (this.hostnameVerifier != null) {
|
||||
httpsConnection.setHostnameVerifier(this.hostnameVerifier);
|
||||
}
|
||||
}
|
||||
return (HttpURLConnection) conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SSLSocketFactory} based on the configuration specified
|
||||
* <p>
|
||||
* Sample properties file:
|
||||
* <pre>
|
||||
* protocol=TLS
|
||||
* keyStoreType=JKS
|
||||
* keyStorePath=/var/secure/location/.keystore
|
||||
* keyStorePass=changeit
|
||||
* certificatePassword=aGoodPass
|
||||
* </pre>
|
||||
* @return the {@link SSLSocketFactory}
|
||||
*/
|
||||
private SSLSocketFactory createSSLSocketFactory() {
|
||||
InputStream keyStoreIS = null;
|
||||
try {
|
||||
final SSLContext sslContext = SSLContext.getInstance(this.sslConfiguration.getProperty("protocol", "SSL"));
|
||||
|
||||
if (this.sslConfiguration.getProperty("keyStoreType") != null) {
|
||||
final KeyStore keyStore = KeyStore.getInstance(this.sslConfiguration.getProperty("keyStoreType"));
|
||||
if (this.sslConfiguration.getProperty("keyStorePath") != null) {
|
||||
keyStoreIS = new FileInputStream(this.sslConfiguration.getProperty("keyStorePath"));
|
||||
if (this.sslConfiguration.getProperty("keyStorePass") != null) {
|
||||
keyStore.load(keyStoreIS, this.sslConfiguration.getProperty("keyStorePass").toCharArray());
|
||||
LOGGER.debug("Keystore has {} keys", keyStore.size());
|
||||
final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(this.sslConfiguration
|
||||
.getProperty("keyManagerType", "SunX509"));
|
||||
keyManager.init(keyStore, this.sslConfiguration.getProperty("certificatePassword")
|
||||
.toCharArray());
|
||||
sslContext.init(keyManager.getKeyManagers(), null, null);
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(keyStoreIS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final HttpsURLConnectionFactory that = (HttpsURLConnectionFactory) o;
|
||||
|
||||
if (!hostnameVerifier.equals(that.hostnameVerifier)) return false;
|
||||
if (!sslConfiguration.equals(that.sslConfiguration)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = hostnameVerifier.hashCode();
|
||||
result = 31 * result + sslConfiguration.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void writeObject(final ObjectOutputStream out) throws IOException {
|
||||
if (this.hostnameVerifier == HttpsURLConnection.getDefaultHostnameVerifier()) {
|
||||
out.writeObject(null);
|
||||
} else {
|
||||
out.writeObject(this.hostnameVerifier);
|
||||
}
|
||||
|
||||
out.writeObject(this.sslConfiguration);
|
||||
|
||||
}
|
||||
|
||||
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
final Object internalHostNameVerifier = in.readObject();
|
||||
if (internalHostNameVerifier == null) {
|
||||
this.hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
} else {
|
||||
this.hostnameVerifier = (HostnameVerifier) internalHostNameVerifier;
|
||||
}
|
||||
|
||||
this.sslConfiguration = (Properties) in.readObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Validates an SSL peer's hostname using a regular expression that a candidate
|
||||
* host must match in order to be verified.
|
||||
*
|
||||
* @author Marvin Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.10
|
||||
*
|
||||
*/
|
||||
public final class RegexHostnameVerifier implements HostnameVerifier, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Allowed hostname pattern */
|
||||
private final Pattern pattern;
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given regular expression.
|
||||
*
|
||||
* @param regex Regular expression describing allowed hosts.
|
||||
*/
|
||||
public RegexHostnameVerifier(final String regex) {
|
||||
this.pattern = Pattern.compile(regex);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
return pattern.matcher(hostname).matches();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Verifies a SSL peer host name based on an explicit whitelist of allowed hosts.
|
||||
*
|
||||
* @author Marvin Addison
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.10
|
||||
*
|
||||
*/
|
||||
public final class WhitelistHostnameVerifier implements HostnameVerifier, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Allowed hosts */
|
||||
private final String[] allowedHosts;
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given array of allowed hosts.
|
||||
*
|
||||
* @param allowed Array of allowed hosts.
|
||||
*/
|
||||
public WhitelistHostnameVerifier(final String[] allowed) {
|
||||
this.allowedHosts = allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given list of allowed hosts.
|
||||
*
|
||||
* @param allowedList Comma-separated list of allowed hosts.
|
||||
*/
|
||||
public WhitelistHostnameVerifier(final String allowedList) {
|
||||
this.allowedHosts = allowedList.split(",\\s*");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean verify(final String hostname, final SSLSession session) {
|
||||
|
||||
for (final String allowedHost : this.allowedHosts) {
|
||||
if (hostname.equalsIgnoreCase(allowedHost)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +1,53 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Abstract filter that contains code that is common to all CAS filters.
|
||||
* <p>
|
||||
* The following filter options can be configured (either at the context-level or filter-level).
|
||||
* <ul>
|
||||
* <li><code>serverName</code> - the name of the CAS server, in the format: localhost:8080 or localhost:8443 or localhost</li>
|
||||
* <li><code>serverName</code> - the name of the CAS client server, in the format: localhost:8080 or localhost:8443 or localhost or https://localhost:8443</li>
|
||||
* <li><code>service</code> - the completely qualified service url, i.e. https://localhost/cas-client/app</li>
|
||||
* </ul>
|
||||
* <p>Please note that one of the two above parameters must be set.</p>
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
|
||||
|
||||
|
||||
/** Represents the constant for where the assertion will be located in memory. */
|
||||
public static final String CONST_CAS_ASSERTION = "_const_cas_assertion_";
|
||||
|
||||
/** Instance of commons logging for logging purposes. */
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
private final Protocol protocol;
|
||||
|
||||
/** Defines the parameter to look for for the artifact. */
|
||||
private String artifactParameterName = "ticket";
|
||||
|
||||
/** Defines the parameter to look for for the service. */
|
||||
private String serviceParameterName = "service";
|
||||
|
||||
/** Sets where response.encodeUrl should be called on service urls when constructed. */
|
||||
private boolean encodeServiceUrl = true;
|
||||
|
||||
|
|
@ -47,64 +59,91 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
|
|||
/** The exact url of the service. */
|
||||
private String service;
|
||||
|
||||
public final void init(final FilterConfig filterConfig) throws ServletException {
|
||||
setServerName(getPropertyFromInitParams(filterConfig, "serverName", null));
|
||||
setService(getPropertyFromInitParams(filterConfig, "service", null));
|
||||
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
|
||||
setServiceParameterName(getPropertyFromInitParams(filterConfig, "serviceParameterName", "service"));
|
||||
setEncodeServiceUrl(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "encodeServiceUrl", "true")));
|
||||
protected AbstractCasFilter(final Protocol protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
initInternal(filterConfig);
|
||||
@Override
|
||||
public final void init(final FilterConfig filterConfig) throws ServletException {
|
||||
super.init(filterConfig);
|
||||
if (!isIgnoreInitConfiguration()) {
|
||||
setServerName(getString(ConfigurationKeys.SERVER_NAME));
|
||||
setService(getString(ConfigurationKeys.SERVICE));
|
||||
setEncodeServiceUrl(getBoolean(ConfigurationKeys.ENCODE_SERVICE_URL));
|
||||
|
||||
initInternal(filterConfig);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
/** Controls the ordering of filter initialization and checking by defining a method that runs before the init. */
|
||||
|
||||
/** Controls the ordering of filter initialization and checking by defining a method that runs before the init.
|
||||
* @param filterConfig the original filter configuration.
|
||||
* @throws ServletException if there is a problem.
|
||||
*
|
||||
*/
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
// template method
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization method. Called by Filter's init method or by Spring.
|
||||
* Initialization method. Called by Filter's init method or by Spring. Similar in concept to the InitializingBean interface's
|
||||
* afterPropertiesSet();
|
||||
*/
|
||||
public void init() {
|
||||
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(this.serviceParameterName, "serviceParameterName cannot be null.");
|
||||
CommonUtils.assertTrue(CommonUtils.isNotEmpty(this.serverName) || CommonUtils.isNotEmpty(this.service), "serverName or service must be set.");
|
||||
CommonUtils.assertTrue(CommonUtils.isNotEmpty(this.serverName) || CommonUtils.isNotEmpty(this.service),
|
||||
"serverName or service must be set.");
|
||||
CommonUtils.assertTrue(CommonUtils.isBlank(this.serverName) || CommonUtils.isBlank(this.service),
|
||||
"serverName and service cannot both be set. You MUST ONLY set one.");
|
||||
}
|
||||
|
||||
public final void destroy() {
|
||||
// empty implementation as most filters won't need this.
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
|
||||
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName, this.encodeServiceUrl);
|
||||
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName,
|
||||
this.protocol.getServiceParameterName(),
|
||||
this.protocol.getArtifactParameterName(), this.encodeServiceUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that trailing slashes should not be used in the serverName. As a convenience for this common misconfiguration, we strip them from the provided
|
||||
* value.
|
||||
*
|
||||
* @param serverName the serverName. If this method is called, this should not be null. This AND service should not be both configured.
|
||||
*/
|
||||
public final void setServerName(final String serverName) {
|
||||
this.serverName = serverName;
|
||||
if (serverName != null && serverName.endsWith("/")) {
|
||||
this.serverName = serverName.substring(0, serverName.length() - 1);
|
||||
logger.info("Eliminated extra slash from serverName [{}]. It is now [{}]", serverName, this.serverName);
|
||||
} else {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setService(final String service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public final void setArtifactParameterName(final String artifactParameterName) {
|
||||
this.artifactParameterName = artifactParameterName;
|
||||
}
|
||||
|
||||
public final void setServiceParameterName(final String serviceParameterName) {
|
||||
this.serviceParameterName = serviceParameterName;
|
||||
}
|
||||
|
||||
public final void setEncodeServiceUrl(final boolean encodeServiceUrl) {
|
||||
this.encodeServiceUrl = encodeServiceUrl;
|
||||
this.encodeServiceUrl = encodeServiceUrl;
|
||||
}
|
||||
|
||||
public final String getArtifactParameterName() {
|
||||
return this.artifactParameterName;
|
||||
protected Protocol getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
public final String getServiceParameterName() {
|
||||
return this.serviceParameterName;
|
||||
/**
|
||||
* Template method to allow you to change how you retrieve the ticket.
|
||||
*
|
||||
* @param request the HTTP ServletRequest. CANNOT be NULL.
|
||||
* @return the ticket if its found, null otherwise.
|
||||
*/
|
||||
protected String retrieveTicketFromRequest(final HttpServletRequest request) {
|
||||
return CommonUtils.safeGetParameter(request, this.protocol.getArtifactParameterName(),
|
||||
Arrays.asList(this.protocol.getArtifactParameterName()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,32 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.jasig.cas.client.configuration.ConfigurationKey;
|
||||
import org.jasig.cas.client.configuration.ConfigurationStrategy;
|
||||
import org.jasig.cas.client.configuration.ConfigurationStrategyName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Abstracts out the ability to configure the filters from the initial properties provided.
|
||||
|
|
@ -12,30 +37,46 @@ import javax.servlet.FilterConfig;
|
|||
*/
|
||||
public abstract class AbstractConfigurationFilter implements Filter {
|
||||
|
||||
/**
|
||||
* Retrieves the property from the FilterConfig. First it checks the FilterConfig's initParameters to see if it
|
||||
* has a value.
|
||||
* If it does, it returns that, otherwise it trieves the ServletContext's initParameters and returns that value if any.
|
||||
*
|
||||
* @param filterConfig the Filter Configuration.
|
||||
* @param propertyName the property to retrieve.
|
||||
* @return the property value, following the above conventions. It will always return the more specific value (i.e.
|
||||
* filter vs. context).
|
||||
*/
|
||||
protected final String getPropertyFromInitParams(final FilterConfig filterConfig, final String propertyName, final String defaultValue) {
|
||||
final String value = filterConfig.getInitParameter(propertyName);
|
||||
private static final String CONFIGURATION_STRATEGY_KEY = "configurationStrategy";
|
||||
|
||||
if (CommonUtils.isNotBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
final String value2 = filterConfig.getServletContext().getInitParameter(propertyName);
|
||||
private boolean ignoreInitConfiguration = false;
|
||||
|
||||
if (CommonUtils.isNotBlank(value2)) {
|
||||
return value2;
|
||||
}
|
||||
private ConfigurationStrategy configurationStrategy;
|
||||
|
||||
return defaultValue;
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
final String configurationStrategyName = filterConfig.getServletContext().getInitParameter(CONFIGURATION_STRATEGY_KEY);
|
||||
this.configurationStrategy = ReflectUtils.newInstance(ConfigurationStrategyName.resolveToConfigurationStrategy(configurationStrategyName));
|
||||
this.configurationStrategy.init(filterConfig, getClass());
|
||||
}
|
||||
|
||||
protected final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
|
||||
return this.configurationStrategy.getBoolean(configurationKey);
|
||||
}
|
||||
|
||||
protected final String getString(final ConfigurationKey<String> configurationKey) {
|
||||
return this.configurationStrategy.getString(configurationKey);
|
||||
}
|
||||
|
||||
protected final long getLong(final ConfigurationKey<Long> configurationKey) {
|
||||
return this.configurationStrategy.getLong(configurationKey);
|
||||
}
|
||||
|
||||
protected final int getInt(final ConfigurationKey<Integer> configurationKey) {
|
||||
return this.configurationStrategy.getInt(configurationKey);
|
||||
}
|
||||
|
||||
protected final <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
|
||||
return this.configurationStrategy.getClass(configurationKey);
|
||||
}
|
||||
|
||||
public final void setIgnoreInitConfiguration(final boolean ignoreInitConfiguration) {
|
||||
this.ignoreInitConfiguration = ignoreInitConfiguration;
|
||||
}
|
||||
|
||||
protected final boolean isIgnoreInitConfiguration() {
|
||||
return this.ignoreInitConfiguration;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,29 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Static holder that places Assertion in a threadlocal.
|
||||
* Static holder that places Assertion in a ThreadLocal.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AssertionHolder {
|
||||
|
|
@ -19,18 +31,21 @@ public class AssertionHolder {
|
|||
/**
|
||||
* ThreadLocal to hold the Assertion for Threads to access.
|
||||
*/
|
||||
private static final ThreadLocal threadLocal = new ThreadLocal();
|
||||
|
||||
private static final ThreadLocal<Assertion> threadLocal = new ThreadLocal<Assertion>();
|
||||
|
||||
/**
|
||||
* Retrieve the assertion from the ThreadLocal.
|
||||
*
|
||||
* @return the Asssertion associated with this thread.
|
||||
*/
|
||||
public static Assertion getAssertion() {
|
||||
return (Assertion) threadLocal.get();
|
||||
return threadLocal.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Assertion to the ThreadLocal.
|
||||
*
|
||||
* @param assertion the assertion to add.
|
||||
*/
|
||||
public static void setAssertion(final Assertion assertion) {
|
||||
threadLocal.set(assertion);
|
||||
|
|
|
|||
|
|
@ -1,39 +1,50 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Places the assertion in a ThreadLocal such that other resources can access it that do not have access to the web tier session.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class AssertionThreadLocalFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
@Override
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpSession session = request.getSession(false);
|
||||
final Assertion assertion = (Assertion) (session != null ? session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : null);
|
||||
final Assertion assertion = (Assertion) (session == null ? request
|
||||
.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session
|
||||
.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));
|
||||
|
||||
try {
|
||||
AssertionHolder.setAssertion(assertion);
|
||||
|
|
@ -43,6 +54,7 @@ public final class AssertionThreadLocalFilter implements Filter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,82 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.validation.ProxyList;
|
||||
import org.jasig.cas.client.validation.ProxyListEditor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Common utilities so that we don't need to include Commons Lang.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class CommonUtils {
|
||||
|
||||
/** Instance of Commons Logging. */
|
||||
private static final Log LOG = LogFactory.getLog(CommonUtils.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtils.class);
|
||||
|
||||
/**
|
||||
* Constant representing the ProxyGrantingTicket IOU Request Parameter.
|
||||
*/
|
||||
private static final String PARAM_PROXY_GRANTING_TICKET_IOU = "pgtIou";
|
||||
|
||||
/**
|
||||
* Constant representing the ProxyGrantingTicket Request Parameter.
|
||||
*/
|
||||
private static final String PARAM_PROXY_GRANTING_TICKET = "pgtId";
|
||||
|
||||
private static final HttpURLConnectionFactory DEFAULT_URL_CONNECTION_FACTORY = new HttpsURLConnectionFactory();
|
||||
|
||||
private static final String SERVICE_PARAMETER_NAMES;
|
||||
|
||||
static {
|
||||
final Set<String> serviceParameterSet = new HashSet<String>(4);
|
||||
for (final Protocol protocol : Protocol.values()) {
|
||||
serviceParameterSet.add(protocol.getServiceParameterName());
|
||||
}
|
||||
SERVICE_PARAMETER_NAMES = serviceParameterSet.toString()
|
||||
.replaceAll("\\[|\\]", "")
|
||||
.replaceAll("\\s", "");
|
||||
}
|
||||
|
||||
private CommonUtils() {
|
||||
// nothing to do
|
||||
|
|
@ -47,10 +99,10 @@ public final class CommonUtils {
|
|||
* Check whether the collection is null or empty. If it is, throw an
|
||||
* exception and display the message.
|
||||
*
|
||||
* @param c the collecion to check.
|
||||
* @param c the collection to check.
|
||||
* @param message the message to display if the object is null.
|
||||
*/
|
||||
public static void assertNotEmpty(final Collection c, final String message) {
|
||||
public static void assertNotEmpty(final Collection<?> c, final String message) {
|
||||
assertNotNull(c, message);
|
||||
if (c.isEmpty()) {
|
||||
throw new IllegalArgumentException(message);
|
||||
|
|
@ -61,7 +113,7 @@ public final class CommonUtils {
|
|||
* Assert that the statement is true, otherwise throw an exception with the
|
||||
* provided message.
|
||||
*
|
||||
* @param cond the codition to assert is true.
|
||||
* @param cond the condition to assert is true.
|
||||
* @param message the message to display if the condition is not true.
|
||||
*/
|
||||
public static void assertTrue(final boolean cond, final String message) {
|
||||
|
|
@ -70,6 +122,20 @@ public final class CommonUtils {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the statement is true, otherwise throw an exception with the
|
||||
* provided message.
|
||||
*
|
||||
* @param cond the condition to assert is false.
|
||||
* @param message the message to display if the condition is not false.
|
||||
*/
|
||||
public static void assertFalse(final boolean cond, final String message) {
|
||||
if (cond) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the String is null or of length 0.
|
||||
*
|
||||
|
|
@ -77,7 +143,7 @@ public final class CommonUtils {
|
|||
* @return true if its null or length of 0, false otherwise.
|
||||
*/
|
||||
public static boolean isEmpty(final String string) {
|
||||
return string == null || string.length() == 0;
|
||||
return string == null || string.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,7 +165,7 @@ public final class CommonUtils {
|
|||
* @return true if its blank, false otherwise.
|
||||
*/
|
||||
public static boolean isBlank(final String string) {
|
||||
return isEmpty(string) || string.trim().length() == 0;
|
||||
return isEmpty(string) || string.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,80 +187,561 @@ public final class CommonUtils {
|
|||
* @param serviceUrl the actual service's url.
|
||||
* @param renew whether we should send renew or not.
|
||||
* @param gateway where we should send gateway or not.
|
||||
* @param method the method used by the CAS server to send the user back to the application.
|
||||
* @return the fully constructed redirect url.
|
||||
*/
|
||||
public static final String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName, final String serviceUrl, final boolean renew, final boolean gateway) {
|
||||
public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName,
|
||||
final String serviceUrl, final boolean renew, final boolean gateway, final String method) {
|
||||
return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "="
|
||||
+ urlEncode(serviceUrl) + (renew ? "&renew=true" : "") + (gateway ? "&gateway=true" : "")
|
||||
+ (method != null ? "&method=" + method : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct redirect url to a CAS server.
|
||||
*
|
||||
* @param casServerLoginUrl the cas server login url
|
||||
* @param serviceParameterName the service parameter name
|
||||
* @param serviceUrl the service url
|
||||
* @param renew the renew
|
||||
* @param gateway the gateway
|
||||
* @return the string
|
||||
*/
|
||||
public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName,
|
||||
final String serviceUrl, final boolean renew, final boolean gateway) {
|
||||
return constructRedirectUrl(casServerLoginUrl, serviceParameterName, serviceUrl, renew, gateway, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Url encode a value using UTF-8 encoding.
|
||||
*
|
||||
* @param value the value to encode.
|
||||
* @return the encoded value.
|
||||
*/
|
||||
public static String urlEncode(final String value) {
|
||||
try {
|
||||
return casServerLoginUrl + "?" + serviceParameterName + "="
|
||||
+ URLEncoder.encode(serviceUrl, "UTF-8")
|
||||
+ (renew ? "&renew=true" : "")
|
||||
+ (gateway ? "&gateway=true" : "");
|
||||
return URLEncoder.encode(value, "UTF-8");
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
public static void readAndRespondToProxyReceptorRequest(final HttpServletRequest request,
|
||||
final HttpServletResponse response, final ProxyGrantingTicketStorage proxyGrantingTicketStorage)
|
||||
throws IOException {
|
||||
final String proxyGrantingTicketIou = request.getParameter(PARAM_PROXY_GRANTING_TICKET_IOU);
|
||||
|
||||
final String proxyGrantingTicket = request.getParameter(PARAM_PROXY_GRANTING_TICKET);
|
||||
|
||||
if (CommonUtils.isBlank(proxyGrantingTicket) || CommonUtils.isBlank(proxyGrantingTicketIou)) {
|
||||
response.getWriter().write("");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug("Received proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]", proxyGrantingTicket,
|
||||
proxyGrantingTicketIou);
|
||||
|
||||
proxyGrantingTicketStorage.save(proxyGrantingTicketIou, proxyGrantingTicket);
|
||||
|
||||
LOGGER.debug("Successfully saved proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]",
|
||||
proxyGrantingTicket, proxyGrantingTicketIou);
|
||||
|
||||
response.getWriter().write("<?xml version=\"1.0\"?>");
|
||||
response.getWriter().write("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\" />");
|
||||
}
|
||||
|
||||
protected static String findMatchingServerName(final HttpServletRequest request, final String serverName) {
|
||||
final String[] serverNames = serverName.split(" ");
|
||||
|
||||
if (serverNames.length == 0 || serverNames.length == 1) {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
final String host = request.getHeader("Host");
|
||||
final String xHost = request.getHeader("X-Forwarded-Host");
|
||||
|
||||
final String comparisonHost;
|
||||
comparisonHost = (xHost != null) ? xHost : host;
|
||||
|
||||
if (comparisonHost == null) {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
for (final String server : serverNames) {
|
||||
final String lowerCaseServer = server.toLowerCase();
|
||||
|
||||
if (lowerCaseServer.contains(comparisonHost)) {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
||||
return serverNames[0];
|
||||
}
|
||||
|
||||
private static boolean requestIsOnStandardPort(final HttpServletRequest request) {
|
||||
final int serverPort = request.getServerPort();
|
||||
return serverPort == 80 || serverPort == 443;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a service url from the HttpServletRequest or from the given
|
||||
* serviceUrl. Prefers the serviceUrl provided if both a serviceUrl and a
|
||||
* serviceName. Compiles a list of all service parameters for supported protocols
|
||||
* and removes them all from the query string.
|
||||
*
|
||||
* @param request the HttpServletRequest
|
||||
* @param response the HttpServletResponse
|
||||
* @param service the configured service url (this will be used if not null)
|
||||
* @param serverNames the server name to use to construct the service url if the service param is empty. Note, prior to CAS Client 3.3, this was a single value.
|
||||
* As of 3.3, it can be a space-separated value. We keep it as a single value, but will convert it to an array internally to get the matching value. This keeps backward compatability with anything using this public
|
||||
* method.
|
||||
* @param artifactParameterName the artifact parameter name to remove (i.e. ticket)
|
||||
* @param encode whether to encode the url or not (i.e. Jsession).
|
||||
* @return the service url to use.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final String service, final String serverNames,
|
||||
final String artifactParameterName, final boolean encode) {
|
||||
return constructServiceUrl(request, response, service, serverNames, SERVICE_PARAMETER_NAMES
|
||||
, artifactParameterName, encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a service url from the HttpServletRequest or from the given
|
||||
* serviceUrl. Prefers the serviceUrl provided if both a serviceUrl and a
|
||||
* serviceName.
|
||||
*
|
||||
* @param request the HttpServletRequest
|
||||
* @param request the HttpServletRequest
|
||||
* @param response the HttpServletResponse
|
||||
* @param service the configured service url (this will be used if not null)
|
||||
* @param serverNames the server name to use to construct the service url if the service param is empty. Note, prior to CAS Client 3.3, this was a single value.
|
||||
* As of 3.3, it can be a space-separated value. We keep it as a single value, but will convert it to an array internally to get the matching value. This keeps backward compatability with anything using this public
|
||||
* method.
|
||||
* @param serviceParameterName the service parameter name to remove (i.e. service)
|
||||
* @param artifactParameterName the artifact parameter name to remove (i.e. ticket)
|
||||
* @param encode whether to encode the url or not (i.e. Jsession).
|
||||
* @return the service url to use.
|
||||
*/
|
||||
public static final String constructServiceUrl(final HttpServletRequest request,
|
||||
final HttpServletResponse response, final String service, final String serverName, final String artifactParameterName, final boolean encode) {
|
||||
public static String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final String service, final String serverNames, final String serviceParameterName,
|
||||
final String artifactParameterName, final boolean encode) {
|
||||
if (CommonUtils.isNotBlank(service)) {
|
||||
return encode ? response.encodeURL(service) : service;
|
||||
}
|
||||
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
final String serverName = findMatchingServerName(request, serverNames);
|
||||
final URIBuilder originalRequestUrl = new URIBuilder(request.getRequestURL().toString(), encode);
|
||||
originalRequestUrl.setParameters(request.getQueryString());
|
||||
|
||||
synchronized (buffer) {
|
||||
if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
|
||||
buffer.append(request.isSecure() ? "https://" : "http://");
|
||||
}
|
||||
final URIBuilder builder;
|
||||
if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
|
||||
final String scheme = request.isSecure() ? "https://" : "http://";
|
||||
builder = new URIBuilder(scheme + serverName, encode);
|
||||
} else {
|
||||
builder = new URIBuilder(serverName, encode);
|
||||
}
|
||||
|
||||
buffer.append(serverName);
|
||||
buffer.append(request.getRequestURI());
|
||||
if (builder.getPort() == -1 && !requestIsOnStandardPort(request)) {
|
||||
builder.setPort(request.getServerPort());
|
||||
}
|
||||
|
||||
if (CommonUtils.isNotBlank(request.getQueryString())) {
|
||||
final int location = request.getQueryString().indexOf(
|
||||
artifactParameterName + "=");
|
||||
builder.setEncodedPath(builder.getEncodedPath() + request.getRequestURI());
|
||||
|
||||
if (location == 0) {
|
||||
final String returnValue = encode ? response.encodeURL(buffer
|
||||
.toString()): buffer.toString();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("serviceUrl generated: " + returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
buffer.append("?");
|
||||
|
||||
if (location == -1) {
|
||||
buffer.append(request.getQueryString());
|
||||
} else if (location > 0) {
|
||||
final int actualLocation = request.getQueryString()
|
||||
.indexOf("&" + artifactParameterName + "=");
|
||||
|
||||
if (actualLocation == -1) {
|
||||
buffer.append(request.getQueryString());
|
||||
} else if (actualLocation > 0) {
|
||||
buffer.append(request.getQueryString().substring(0,
|
||||
actualLocation));
|
||||
final List<String> serviceParameterNames = Arrays.asList(serviceParameterName.split(","));
|
||||
if (!serviceParameterNames.isEmpty() && !originalRequestUrl.getQueryParams().isEmpty()) {
|
||||
for (final URIBuilder.BasicNameValuePair pair : originalRequestUrl.getQueryParams()) {
|
||||
final String name = pair.getName();
|
||||
if (!name.equals(artifactParameterName) && !serviceParameterNames.contains(name)) {
|
||||
if (name.contains("&") || name.contains("=")) {
|
||||
final URIBuilder encodedParamBuilder = new URIBuilder();
|
||||
encodedParamBuilder.setParameters(name);
|
||||
for (final URIBuilder.BasicNameValuePair pair2 : encodedParamBuilder.getQueryParams()) {
|
||||
final String name2 = pair2.getName();
|
||||
if (!name2.equals(artifactParameterName) && !serviceParameterNames.contains(name2)) {
|
||||
builder.addParameter(name2, pair2.getValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.addParameter(name, pair.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String returnValue = encode ? response.encodeURL(buffer.toString()) : buffer.toString();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("serviceUrl generated: " + returnValue);
|
||||
}
|
||||
final String result = builder.toString();
|
||||
final String returnValue = encode ? response.encodeURL(result) : result;
|
||||
LOGGER.debug("serviceUrl generated: {}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe method for retrieving a parameter from the request without disrupting the reader UNLESS the parameter
|
||||
* actually exists in the query string.
|
||||
* <p>
|
||||
* Note, this does not work for POST Requests for "logoutRequest". It works for all other CAS POST requests because the
|
||||
* parameter is ALWAYS in the GET request.
|
||||
* <p>
|
||||
* If we see the "logoutRequest" parameter we MUST treat it as if calling the standard request.getParameter.
|
||||
* <p>
|
||||
* Note, that as of 3.3.0, we've made it more generic.
|
||||
* </p>
|
||||
*
|
||||
* @param request the request to check.
|
||||
* @param parameter the parameter to look for.
|
||||
* @return the value of the parameter.
|
||||
*/
|
||||
public static String safeGetParameter(final HttpServletRequest request, final String parameter,
|
||||
final List<String> parameters) {
|
||||
if ("POST".equals(request.getMethod()) && parameters.contains(parameter)) {
|
||||
LOGGER.debug("safeGetParameter called on a POST HttpServletRequest for Restricted Parameters. Cannot complete check safely. Reverting to standard behavior for this Parameter");
|
||||
return request.getParameter(parameter);
|
||||
}
|
||||
return request.getQueryString() == null || !request.getQueryString().contains(parameter) ? null : request
|
||||
.getParameter(parameter);
|
||||
}
|
||||
|
||||
public static String safeGetParameter(final HttpServletRequest request, final String parameter) {
|
||||
return safeGetParameter(request, parameter, Arrays.asList("logoutRequest"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param constructedUrl the url to contact.
|
||||
* @param encoding the encoding to use.
|
||||
* @return the response.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getResponseFromServer(final String constructedUrl, final String encoding) {
|
||||
try {
|
||||
return getResponseFromServer(new URL(constructedUrl), DEFAULT_URL_CONNECTION_FACTORY, encoding);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getResponseFromServer(final URL constructedUrl, final String encoding) {
|
||||
return getResponseFromServer(constructedUrl, DEFAULT_URL_CONNECTION_FACTORY, encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param constructedUrl the url to contact.
|
||||
* @param factory connection factory to prepare the URL connection instance
|
||||
* @param encoding the encoding to use.
|
||||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory,
|
||||
final String encoding) {
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
InputStreamReader in = null;
|
||||
try {
|
||||
conn = factory.buildHttpURLConnection(constructedUrl.openConnection());
|
||||
|
||||
if (CommonUtils.isEmpty(encoding)) {
|
||||
in = new InputStreamReader(conn.getInputStream());
|
||||
} else {
|
||||
in = new InputStreamReader(conn.getInputStream(), encoding);
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder(255);
|
||||
int byteRead;
|
||||
while ((byteRead = in.read()) != -1) {
|
||||
builder.append((char) byteRead);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
} catch (final RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final SSLException e) {
|
||||
LOGGER.error("SSL error getting response from host: {} : Error Message: {}", constructedUrl.getHost(), e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error("Error getting response from host: [{}] with path: [{}] and protocol: [{}] Error Message: {}",
|
||||
constructedUrl.getHost(), constructedUrl.getPath(), constructedUrl.getProtocol(), e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(in);
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ProxyList createProxyList(final String proxies) {
|
||||
if (CommonUtils.isBlank(proxies)) {
|
||||
return new ProxyList();
|
||||
}
|
||||
|
||||
final ProxyListEditor editor = new ProxyListEditor();
|
||||
editor.setAsText(proxies);
|
||||
return (ProxyList) editor.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the redirect message and captures the exceptions that we can't possibly do anything with.
|
||||
*
|
||||
* @param response the HttpServletResponse. CANNOT be NULL.
|
||||
* @param url the url to redirect to.
|
||||
*/
|
||||
public static void sendRedirect(final HttpServletResponse response, final String url) {
|
||||
try {
|
||||
response.sendRedirect(url);
|
||||
} catch (final IOException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally close a {@link Closeable}. Equivalent to {@link java.io.Closeable#close()}close(), except any exceptions
|
||||
* will be ignored. This is typically used in finally blocks.
|
||||
* @param resource the resource to close
|
||||
*/
|
||||
public static void closeQuietly(final Closeable resource) {
|
||||
try {
|
||||
if (resource != null) {
|
||||
resource.close();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts a String to a boolean (optimised for performance).</p>
|
||||
*
|
||||
* <p>{@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'}
|
||||
* (case insensitive) will return {@code true}. Otherwise,
|
||||
* {@code false} is returned.</p>
|
||||
*
|
||||
* <p>This method performs 4 times faster (JDK1.4) than
|
||||
* {@code Boolean.valueOf(String)}. However, this method accepts
|
||||
* 'on' and 'yes', 't', 'y' as true values.
|
||||
*
|
||||
* <pre>
|
||||
* BooleanUtils.toBoolean(null) = false
|
||||
* BooleanUtils.toBoolean("true") = true
|
||||
* BooleanUtils.toBoolean("TRUE") = true
|
||||
* BooleanUtils.toBoolean("tRUe") = true
|
||||
* BooleanUtils.toBoolean("on") = true
|
||||
* BooleanUtils.toBoolean("yes") = true
|
||||
* BooleanUtils.toBoolean("false") = false
|
||||
* BooleanUtils.toBoolean("x gti") = false
|
||||
* BooleanUtils.toBooleanObject("y") = true
|
||||
* BooleanUtils.toBooleanObject("n") = false
|
||||
* BooleanUtils.toBooleanObject("t") = true
|
||||
* BooleanUtils.toBooleanObject("f") = false
|
||||
* </pre>
|
||||
*
|
||||
* @param str the String to check
|
||||
* @return the boolean value of the string, {@code false} if no match or the String is null
|
||||
*/
|
||||
public static boolean toBoolean(final String str) {
|
||||
return toBooleanObject(str) == Boolean.TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts a String to a Boolean.</p>
|
||||
*
|
||||
* <p>{@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'}
|
||||
* (case insensitive) will return {@code true}.
|
||||
* {@code 'false'}, {@code 'off'}, {@code 'n'}, {@code 'f'} or {@code 'no'}
|
||||
* (case insensitive) will return {@code false}.
|
||||
* Otherwise, {@code null} is returned.</p>
|
||||
*
|
||||
* <p>NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean. </p>
|
||||
*
|
||||
* <pre>
|
||||
* // N.B. case is not significant
|
||||
* BooleanUtils.toBooleanObject(null) = null
|
||||
* BooleanUtils.toBooleanObject("true") = Boolean.TRUE
|
||||
* BooleanUtils.toBooleanObject("T") = Boolean.TRUE // i.e. T[RUE]
|
||||
* BooleanUtils.toBooleanObject("false") = Boolean.FALSE
|
||||
* BooleanUtils.toBooleanObject("f") = Boolean.FALSE // i.e. f[alse]
|
||||
* BooleanUtils.toBooleanObject("No") = Boolean.FALSE
|
||||
* BooleanUtils.toBooleanObject("n") = Boolean.FALSE // i.e. n[o]
|
||||
* BooleanUtils.toBooleanObject("on") = Boolean.TRUE
|
||||
* BooleanUtils.toBooleanObject("ON") = Boolean.TRUE
|
||||
* BooleanUtils.toBooleanObject("off") = Boolean.FALSE
|
||||
* BooleanUtils.toBooleanObject("oFf") = Boolean.FALSE
|
||||
* BooleanUtils.toBooleanObject("yes") = Boolean.TRUE
|
||||
* BooleanUtils.toBooleanObject("Y") = Boolean.TRUE // i.e. Y[ES]
|
||||
* BooleanUtils.toBooleanObject("blue") = null
|
||||
* BooleanUtils.toBooleanObject("true ") = null // trailing space (too long)
|
||||
* BooleanUtils.toBooleanObject("ono") = null // does not match on or no
|
||||
* </pre>
|
||||
*
|
||||
* @param str the String to check; upper and lower case are treated as the same
|
||||
* @return the Boolean value of the string, {@code null} if no match or {@code null} input
|
||||
*/
|
||||
public static Boolean toBooleanObject(final String str) {
|
||||
// Previously used equalsIgnoreCase, which was fast for interned 'true'.
|
||||
// Non interned 'true' matched 15 times slower.
|
||||
//
|
||||
// Optimisation provides same performance as before for interned 'true'.
|
||||
// Similar performance for null, 'false', and other strings not length 2/3/4.
|
||||
// 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower.
|
||||
if (str == "true") {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
switch (str.length()) {
|
||||
case 1: {
|
||||
final char ch0 = str.charAt(0);
|
||||
if (ch0 == 'y' || ch0 == 'Y' ||
|
||||
ch0 == 't' || ch0 == 'T') {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (ch0 == 'n' || ch0 == 'N' ||
|
||||
ch0 == 'f' || ch0 == 'F') {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
final char ch0 = str.charAt(0);
|
||||
final char ch1 = str.charAt(1);
|
||||
if ((ch0 == 'o' || ch0 == 'O') &&
|
||||
(ch1 == 'n' || ch1 == 'N')) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ((ch0 == 'n' || ch0 == 'N') &&
|
||||
(ch1 == 'o' || ch1 == 'O')) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
final char ch0 = str.charAt(0);
|
||||
final char ch1 = str.charAt(1);
|
||||
final char ch2 = str.charAt(2);
|
||||
if ((ch0 == 'y' || ch0 == 'Y') &&
|
||||
(ch1 == 'e' || ch1 == 'E') &&
|
||||
(ch2 == 's' || ch2 == 'S')) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if ((ch0 == 'o' || ch0 == 'O') &&
|
||||
(ch1 == 'f' || ch1 == 'F') &&
|
||||
(ch2 == 'f' || ch2 == 'F')) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
final char ch0 = str.charAt(0);
|
||||
final char ch1 = str.charAt(1);
|
||||
final char ch2 = str.charAt(2);
|
||||
final char ch3 = str.charAt(3);
|
||||
if ((ch0 == 't' || ch0 == 'T') &&
|
||||
(ch1 == 'r' || ch1 == 'R') &&
|
||||
(ch2 == 'u' || ch2 == 'U') &&
|
||||
(ch3 == 'e' || ch3 == 'E')) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
final char ch0 = str.charAt(0);
|
||||
final char ch1 = str.charAt(1);
|
||||
final char ch2 = str.charAt(2);
|
||||
final char ch3 = str.charAt(3);
|
||||
final char ch4 = str.charAt(4);
|
||||
if ((ch0 == 'f' || ch0 == 'F') &&
|
||||
(ch1 == 'a' || ch1 == 'A') &&
|
||||
(ch2 == 'l' || ch2 == 'L') &&
|
||||
(ch3 == 's' || ch3 == 'S') &&
|
||||
(ch4 == 'e' || ch4 == 'E')) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to a <code>long</code>, returning a
|
||||
* default value if the conversion fails.</p>
|
||||
*
|
||||
* <p>If the string is <code>null</code>, the default value is returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* NumberUtils.toLong(null, 1L) = 1L
|
||||
* NumberUtils.toLong("", 1L) = 1L
|
||||
* NumberUtils.toLong("1", 0L) = 1L
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @param defaultValue the default value
|
||||
* @return the long represented by the string, or the default if conversion fails
|
||||
*/
|
||||
public static long toLong(final String str, final long defaultValue) {
|
||||
if (str == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(str);
|
||||
} catch (final NumberFormatException nfe) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert a <code>String</code> to an <code>int</code>, returning a
|
||||
* default value if the conversion fails.</p>
|
||||
*
|
||||
* <p>If the string is <code>null</code>, the default value is returned.</p>
|
||||
*
|
||||
* <pre>
|
||||
* NumberUtils.toInt(null, 1) = 1
|
||||
* NumberUtils.toInt("", 1) = 1
|
||||
* NumberUtils.toInt("1", 0) = 1
|
||||
* </pre>
|
||||
*
|
||||
* @param str the string to convert, may be null
|
||||
* @param defaultValue the default value
|
||||
* @return the int represented by the string, or the default if conversion fails
|
||||
*/
|
||||
public static int toInt(final String str, final int defaultValue) {
|
||||
if (str == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (final NumberFormatException nfe) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string as-is, unless it's <code>null</code>;
|
||||
* in this case an empty string is returned.
|
||||
*
|
||||
* @param string a possibly <code>null</code> string
|
||||
* @return a non-<code>null</code> string
|
||||
*/
|
||||
public static String nullToEmpty(final String string) {
|
||||
return string == null ? "" : string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trailing slash to the given uri, if it doesn't already have one.
|
||||
*
|
||||
* @param uri a string that may or may not end with a slash
|
||||
* @return the same string, except with a slash suffix (if necessary).
|
||||
*/
|
||||
public static String addTrailingSlash(final String uri) {
|
||||
return uri.endsWith("/") ? uri : uri + "/";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,29 @@
|
|||
/*
|
||||
* Copyright 2006 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A Delegating Filter looks up a parameter in the request object and matches
|
||||
|
|
@ -24,7 +31,6 @@ import java.util.Map;
|
|||
* the associated filter is executed. Otherwise, the normal chain is executed.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2006-09-26 14:22:30 -0400 (Tue, 26 Sep 2006) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class DelegatingFilter implements Filter {
|
||||
|
|
@ -32,7 +38,7 @@ public final class DelegatingFilter implements Filter {
|
|||
/**
|
||||
* Instance of Commons Logging.
|
||||
*/
|
||||
private final Log log = LogFactory.getLog(this.getClass());
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
/**
|
||||
* The request parameter to look for in the Request object.
|
||||
|
|
@ -42,7 +48,7 @@ public final class DelegatingFilter implements Filter {
|
|||
/**
|
||||
* The map of filters to delegate to and the criteria (as key).
|
||||
*/
|
||||
private final Map delegators;
|
||||
private final Map<String, Filter> delegators;
|
||||
|
||||
/**
|
||||
* The default filter to use if there is no match.
|
||||
|
|
@ -55,25 +61,15 @@ public final class DelegatingFilter implements Filter {
|
|||
*/
|
||||
private final boolean exactMatch;
|
||||
|
||||
public DelegatingFilter(final String requestParameterName, final Map delegators, final boolean exactMatch) {
|
||||
public DelegatingFilter(final String requestParameterName, final Map<String, Filter> delegators,
|
||||
final boolean exactMatch) {
|
||||
this(requestParameterName, delegators, exactMatch, null);
|
||||
}
|
||||
|
||||
public DelegatingFilter(final String requestParameterName, final Map delegators, final boolean exactMatch, final Filter defaultFilter) {
|
||||
CommonUtils.assertNotNull(requestParameterName,
|
||||
"requestParameterName cannot be null.");
|
||||
CommonUtils.assertTrue(!delegators.isEmpty(),
|
||||
"delegators cannot be empty.");
|
||||
|
||||
for (final Iterator iter = delegators.keySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
final Object object = delegators.get(iter.next());
|
||||
|
||||
if (!Filter.class.isAssignableFrom(object.getClass())) {
|
||||
throw new IllegalArgumentException(
|
||||
"All value objects in the delegators map must be filters.");
|
||||
}
|
||||
}
|
||||
public DelegatingFilter(final String requestParameterName, final Map<String, Filter> delegators,
|
||||
final boolean exactMatch, final Filter defaultFilter) {
|
||||
CommonUtils.assertNotNull(requestParameterName, "requestParameterName cannot be null.");
|
||||
CommonUtils.assertTrue(!delegators.isEmpty(), "delegators cannot be empty.");
|
||||
|
||||
this.requestParameterName = requestParameterName;
|
||||
this.delegators = delegators;
|
||||
|
|
@ -81,39 +77,30 @@ public final class DelegatingFilter implements Filter {
|
|||
this.exactMatch = exactMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
public void doFilter(final ServletRequest request,
|
||||
final ServletResponse response, final FilterChain filterChain)
|
||||
@Override
|
||||
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
final String parameter = request
|
||||
.getParameter(this.requestParameterName);
|
||||
final String parameter = CommonUtils.safeGetParameter((HttpServletRequest) request, this.requestParameterName);
|
||||
|
||||
if (CommonUtils.isNotEmpty(parameter)) {
|
||||
for (final Iterator iter = this.delegators.keySet().iterator(); iter
|
||||
.hasNext();) {
|
||||
final String key = (String) iter.next();
|
||||
|
||||
if ((parameter.equals(key) && this.exactMatch)
|
||||
|| (parameter.matches(key) && !this.exactMatch)) {
|
||||
final Filter filter = (Filter) this.delegators.get(key);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Match found for parameter ["
|
||||
+ this.requestParameterName + "] with value ["
|
||||
+ parameter + "]. Delegating to filter ["
|
||||
+ filter.getClass().getName() + "]");
|
||||
}
|
||||
for (final String key : this.delegators.keySet()) {
|
||||
if ((parameter.equals(key) && this.exactMatch) || (parameter.matches(key) && !this.exactMatch)) {
|
||||
final Filter filter = this.delegators.get(key);
|
||||
logger.debug("Match found for parameter [{}] with value [{}]. Delegating to filter [{}]",
|
||||
this.requestParameterName, parameter, filter.getClass().getName());
|
||||
filter.doFilter(request, response, filterChain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("No match found for parameter [" + this.requestParameterName
|
||||
+ "] with value [" + parameter + "]");
|
||||
logger.debug("No match found for parameter [{}] with value [{}]", this.requestParameterName, parameter);
|
||||
|
||||
if (this.defaultFilter != null) {
|
||||
this.defaultFilter.doFilter(request, response, filterChain);
|
||||
|
|
@ -122,6 +109,7 @@ public final class DelegatingFilter implements Filter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
// nothing to do here.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Filters that redirects to the supplied url based on an exception. Exceptions and the urls are configured via
|
||||
* init filter name/param values.
|
||||
* <p/>
|
||||
* If there is an exact match the filter uses that value. If there's a non-exact match (i.e. inheritance), then the filter
|
||||
* uses the last value that matched.
|
||||
* <p/>
|
||||
* If there is no match it will redirect to a default error page. The default exception is configured via the "defaultErrorRedirectPage" property.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.4
|
||||
*/
|
||||
public final class ErrorRedirectFilter implements Filter {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final List<ErrorHolder> errors = new ArrayList<ErrorHolder>();
|
||||
|
||||
private String defaultErrorRedirectPage;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
final HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (final Exception e) {
|
||||
final Throwable t = extractErrorToCompare(e);
|
||||
ErrorHolder currentMatch = null;
|
||||
for (final ErrorHolder errorHolder : this.errors) {
|
||||
if (errorHolder.exactMatch(t)) {
|
||||
currentMatch = errorHolder;
|
||||
break;
|
||||
} else if (errorHolder.inheritanceMatch(t)) {
|
||||
currentMatch = errorHolder;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMatch != null) {
|
||||
httpResponse.sendRedirect(currentMatch.getUrl());
|
||||
} else {
|
||||
httpResponse.sendRedirect(defaultErrorRedirectPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which error to use for comparison. If there is an {@link Throwable#getCause()} then that will be used. Otherwise, the original throwable is used.
|
||||
*
|
||||
* @param throwable the throwable to look for a root cause.
|
||||
* @return the throwable to use for comparison. MUST NOT BE NULL.
|
||||
*/
|
||||
private Throwable extractErrorToCompare(final Throwable throwable) {
|
||||
final Throwable cause = throwable.getCause();
|
||||
|
||||
if (cause != null) {
|
||||
return cause;
|
||||
}
|
||||
|
||||
return throwable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
this.defaultErrorRedirectPage = filterConfig.getInitParameter("defaultErrorRedirectPage");
|
||||
|
||||
final Enumeration<?> enumeration = filterConfig.getInitParameterNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
final String className = (String) enumeration.nextElement();
|
||||
try {
|
||||
if (!className.equals("defaultErrorRedirectPage")) {
|
||||
this.errors.add(new ErrorHolder(className, filterConfig.getInitParameter(className)));
|
||||
}
|
||||
} catch (final ClassNotFoundException e) {
|
||||
logger.warn("Class [{}] cannot be found in ClassLoader. Ignoring.", className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final class ErrorHolder {
|
||||
|
||||
private Class<?> className;
|
||||
|
||||
private String url;
|
||||
|
||||
protected ErrorHolder(final String className, final String url) throws ClassNotFoundException {
|
||||
this.className = Class.forName(className);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean exactMatch(final Throwable e) {
|
||||
return this.className.equals(e.getClass());
|
||||
}
|
||||
|
||||
public boolean inheritanceMatch(final Throwable e) {
|
||||
return className.isAssignableFrom(e.getClass());
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +1,60 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Implementation of a filter that wraps the normal HttpServletRequest with a
|
||||
* wrapper that overrides the getRemoteUser method to retrieve the user from the
|
||||
* CAS Assertion.
|
||||
* wrapper that overrides the following methods to provide data from the
|
||||
* CAS Assertion:
|
||||
* <ul>
|
||||
* <li>{@link HttpServletRequest#getUserPrincipal()}</li>
|
||||
* <li>{@link HttpServletRequest#getRemoteUser()}</li>
|
||||
* <li>{@link HttpServletRequest#isUserInRole(String)}</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* This filter needs to be configured in the chain so that it executes after
|
||||
* both the authentication and the validation filters.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @author Marvin S. Addison
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class HttpServletRequestWrapperFilter implements Filter {
|
||||
public final class HttpServletRequestWrapperFilter extends AbstractConfigurationFilter {
|
||||
|
||||
/** Name of the attribute used to answer role membership queries */
|
||||
private String roleAttribute;
|
||||
|
||||
/** Whether or not to ignore case in role membership queries */
|
||||
private boolean ignoreCase;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// nothing to do
|
||||
}
|
||||
|
|
@ -42,42 +64,95 @@ public final class HttpServletRequestWrapperFilter implements Filter {
|
|||
* <code>request.getRemoteUser</code> to the underlying Assertion object
|
||||
* stored in the user session.
|
||||
*/
|
||||
public void doFilter(final ServletRequest servletRequest,
|
||||
final ServletResponse servletResponse, final FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
final Principal principal = retrievePrincipalFromSessionOrRequest(servletRequest);
|
||||
@Override
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
final AttributePrincipal principal = retrievePrincipalFromSessionOrRequest(servletRequest);
|
||||
|
||||
filterChain.doFilter(new CasHttpServletRequestWrapper(
|
||||
(HttpServletRequest) servletRequest, principal), servletResponse);
|
||||
filterChain.doFilter(new CasHttpServletRequestWrapper((HttpServletRequest) servletRequest, principal),
|
||||
servletResponse);
|
||||
}
|
||||
|
||||
protected Principal retrievePrincipalFromSessionOrRequest(final ServletRequest servletRequest) {
|
||||
protected AttributePrincipal retrievePrincipalFromSessionOrRequest(final ServletRequest servletRequest) {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpSession session = request.getSession(false);
|
||||
final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));
|
||||
final Assertion assertion = (Assertion) (session == null ? request
|
||||
.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session
|
||||
.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION));
|
||||
|
||||
return assertion == null ? null : assertion.getPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
// nothing to do
|
||||
super.init(filterConfig);
|
||||
this.roleAttribute = getString(ConfigurationKeys.ROLE_ATTRIBUTE);
|
||||
this.ignoreCase = getBoolean(ConfigurationKeys.IGNORE_CASE);
|
||||
}
|
||||
|
||||
final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final Principal principal;
|
||||
private final AttributePrincipal principal;
|
||||
|
||||
CasHttpServletRequestWrapper(final HttpServletRequest request, final Principal principal) {
|
||||
CasHttpServletRequestWrapper(final HttpServletRequest request, final AttributePrincipal principal) {
|
||||
super(request);
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteUser() {
|
||||
return getUserPrincipal().getName();
|
||||
return principal != null ? this.principal.getName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(final String role) {
|
||||
if (CommonUtils.isBlank(role)) {
|
||||
logger.debug("No valid role provided. Returning false.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.principal == null) {
|
||||
logger.debug("No Principal in Request. Returning false.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CommonUtils.isBlank(roleAttribute)) {
|
||||
logger.debug("No Role Attribute Configured. Returning false.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final Object value = this.principal.getAttributes().get(roleAttribute);
|
||||
|
||||
if (value instanceof Collection<?>) {
|
||||
for (final Object o : (Collection<?>) value) {
|
||||
if (rolesEqual(role, o)) {
|
||||
logger.debug("User [{}] is in role [{}]: true", getRemoteUser(), role);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isMember = rolesEqual(role, value);
|
||||
logger.debug("User [{}] is in role [{}]: {}", getRemoteUser(), role, isMember);
|
||||
return isMember;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given role is equal to the candidate
|
||||
* role attribute taking into account case sensitivity.
|
||||
*
|
||||
* @param given Role under consideration.
|
||||
* @param candidate Role that the current user possesses.
|
||||
*
|
||||
* @return True if roles are equal, false otherwise.
|
||||
*/
|
||||
private boolean rolesEqual(final String given, final Object candidate) {
|
||||
return ignoreCase ? given.equalsIgnoreCase(candidate.toString()) : given.equals(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* IO utility class.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @since 3.4
|
||||
*/
|
||||
public final class IOUtils {
|
||||
|
||||
/** UTF-8 character set. */
|
||||
public static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
|
||||
private IOUtils() { /** Utility class pattern. */ }
|
||||
|
||||
/**
|
||||
* Reads all data from the given stream as UTF-8 character data and closes it on completion or errors.
|
||||
*
|
||||
* @param in Input stream containing character data.
|
||||
*
|
||||
* @return String of all data in stream.
|
||||
*
|
||||
* @throws IOException On IO errors.
|
||||
*/
|
||||
public static String readString(final InputStream in) throws IOException {
|
||||
return readString(in, UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all data from the given stream as character data in the given character set and closes it on completion
|
||||
* or errors.
|
||||
*
|
||||
* @param in Input stream containing character data.
|
||||
* @param charset Character set of data in stream.
|
||||
*
|
||||
* @return String of all data in stream.
|
||||
*
|
||||
* @throws IOException On IO errors.
|
||||
*/
|
||||
public static String readString(final InputStream in, final Charset charset) throws IOException {
|
||||
final Reader reader = new InputStreamReader(in, charset);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final CharBuffer buffer = CharBuffer.allocate(2048);
|
||||
try {
|
||||
while (reader.read(buffer) > -1) {
|
||||
buffer.flip();
|
||||
builder.append(buffer);
|
||||
}
|
||||
} finally {
|
||||
closeQuietly(reader);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally close a {@link Closeable} resource. Errors on close are ignored.
|
||||
*
|
||||
* @param resource Resource to close.
|
||||
*/
|
||||
public static void closeQuietly(final Closeable resource) {
|
||||
try {
|
||||
if (resource != null) {
|
||||
resource.close();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Namespace context implementation backed by a map of XML prefixes to namespace URIs.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @since 3.4
|
||||
*/
|
||||
public class MapNamespaceContext implements NamespaceContext {
|
||||
|
||||
private final Map<String, String> namespaceMap;
|
||||
|
||||
/**
|
||||
* Creates a new instance from an array of namespace delcarations.
|
||||
*
|
||||
* @param namespaceDeclarations An array of namespace declarations of the form prefix->uri.
|
||||
*/
|
||||
public MapNamespaceContext(final String ... namespaceDeclarations) {
|
||||
namespaceMap = new HashMap<String, String>();
|
||||
int index;
|
||||
String key;
|
||||
String value;
|
||||
for (final String decl : namespaceDeclarations) {
|
||||
index = decl.indexOf('-');
|
||||
key = decl.substring(0, index);
|
||||
value = decl.substring(index + 2);
|
||||
namespaceMap.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from a map.
|
||||
*
|
||||
* @param namespaceMap Map of XML namespace prefixes (keys) to URIs (values).
|
||||
*/
|
||||
public MapNamespaceContext(final Map<String, String> namespaceMap) {
|
||||
this.namespaceMap = namespaceMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespaceURI(final String prefix) {
|
||||
return namespaceMap.get(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix(final String namespaceURI) {
|
||||
for (final Map.Entry<String, String> entry : namespaceMap.entrySet()) {
|
||||
if (entry.getValue().equalsIgnoreCase(namespaceURI)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator getPrefixes(final String namespaceURI) {
|
||||
return Collections.singleton(getPrefix(namespaceURI)).iterator();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Security;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
|
||||
/**
|
||||
* Utility class to parse private keys.
|
||||
*
|
||||
* @author Jerome LELEU
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public class PrivateKeyUtils {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PrivateKeyUtils.class);
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static PrivateKey createKey(final String path, final String algorithm) {
|
||||
final PrivateKey key = readPemPrivateKey(path);
|
||||
if (key == null) {
|
||||
return readDERPrivateKey(path, algorithm);
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
private static PrivateKey readPemPrivateKey(final String path) {
|
||||
LOGGER.debug("Attempting to read as PEM [{}]", path);
|
||||
final File file = new File(path);
|
||||
InputStreamReader isr = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
isr = new FileReader(file);
|
||||
br = new BufferedReader(isr);
|
||||
final PEMParser pp = new PEMParser(br);
|
||||
final PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
|
||||
final KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
|
||||
return kp.getPrivate();
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error("Unable to read key", e);
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (br != null) {
|
||||
br.close();
|
||||
}
|
||||
if (isr != null) {
|
||||
isr.close();
|
||||
}
|
||||
} catch (final IOException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
private static PrivateKey readDERPrivateKey(final String path, final String algorithm) {
|
||||
LOGGER.debug("Attempting to read key as DER [{}]", path);
|
||||
final File file = new File(path);
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
final long byteLength = file.length();
|
||||
final byte[] bytes = new byte[(int) byteLength];
|
||||
fis.read(bytes, 0, (int) byteLength);
|
||||
final PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(bytes);
|
||||
final KeyFactory factory = KeyFactory.getInstance(algorithm);
|
||||
return factory.generatePrivate(privSpec);
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error("Unable to read key", e);
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
} catch (final IOException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Helper class with reflection utility methods.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public final class ReflectUtils {
|
||||
|
||||
private ReflectUtils() {
|
||||
// private constructor to prevent instanciation.
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a class from a String.
|
||||
* @param className the name of the class to create.
|
||||
* @return the class. CANNOT be NULL.
|
||||
* @throws IllegalArgumentException if the className does not exist.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> loadClass(final String className) throws IllegalArgumentException {
|
||||
try {
|
||||
return (Class<T>) Class.forName(className);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(className + " class not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given class by passing the given arguments
|
||||
* to the constructor.
|
||||
* @param className Name of class to be created.
|
||||
* @param args Constructor arguments.
|
||||
* @return New instance of given class.
|
||||
*/
|
||||
public static <T> T newInstance(final String className, final Object... args) {
|
||||
return newInstance(ReflectUtils.<T> loadClass(className), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given class by passing the given arguments
|
||||
* to the constructor.
|
||||
* @param clazz Class of instance to be created.
|
||||
* @param args Constructor arguments.
|
||||
* @return New instance of given class.
|
||||
*/
|
||||
public static <T> T newInstance(final Class<T> clazz, final Object... args) {
|
||||
final Class<?>[] argClasses = new Class[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
argClasses[i] = args[i].getClass();
|
||||
}
|
||||
try {
|
||||
return clazz.getConstructor(argClasses).newInstance(args);
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalArgumentException("Error creating new instance of " + clazz, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property descriptor for the named property on the given class.
|
||||
* @param clazz Class to which property belongs.
|
||||
* @param propertyName Name of property.
|
||||
* @return Property descriptor for given property or null if no property with given
|
||||
* name exists in given class.
|
||||
*/
|
||||
public static PropertyDescriptor getPropertyDescriptor(final Class<?> clazz, final String propertyName) {
|
||||
try {
|
||||
return getPropertyDescriptor(Introspector.getBeanInfo(clazz), propertyName);
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Failed getting bean info for " + clazz, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property descriptor for the named property from the bean info describing
|
||||
* a particular class to which property belongs.
|
||||
* @param info Bean info describing class to which property belongs.
|
||||
* @param propertyName Name of property.
|
||||
* @return Property descriptor for given property or null if no property with given
|
||||
* name exists.
|
||||
*/
|
||||
public static PropertyDescriptor getPropertyDescriptor(final BeanInfo info, final String propertyName) {
|
||||
for (int i = 0; i < info.getPropertyDescriptors().length; i++) {
|
||||
final PropertyDescriptor pd = info.getPropertyDescriptors()[i];
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given property on the target JavaBean using bean instrospection.
|
||||
* @param propertyName Property to set.
|
||||
* @param value Property value to set.
|
||||
* @param target Target java bean on which to set property.
|
||||
*/
|
||||
public static void setProperty(final String propertyName, final Object value, final Object target) {
|
||||
try {
|
||||
setProperty(propertyName, value, target, Introspector.getBeanInfo(target.getClass()));
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Failed getting bean info on target JavaBean " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given property on the target JavaBean using bean instrospection.
|
||||
* @param propertyName Property to set.
|
||||
* @param value Property value to set.
|
||||
* @param target Target JavaBean on which to set property.
|
||||
* @param info BeanInfo describing the target JavaBean.
|
||||
*/
|
||||
public static void setProperty(final String propertyName, final Object value, final Object target,
|
||||
final BeanInfo info) {
|
||||
try {
|
||||
final PropertyDescriptor pd = getPropertyDescriptor(info, propertyName);
|
||||
pd.getWriteMethod().invoke(target, value);
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new RuntimeException("Error setting property " + propertyName, e.getCause());
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Error setting property " + propertyName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given declared field on the target object or any of its superclasses.
|
||||
*
|
||||
* @param fieldName Name of field to get.
|
||||
* @param target Target object that possesses field.
|
||||
*
|
||||
* @return Field value.
|
||||
*/
|
||||
public static Object getField(final String fieldName, final Object target) {
|
||||
Class<?> clazz = target.getClass();
|
||||
Field field = null;
|
||||
do {
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
} catch (final NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
} while (field == null && clazz != null);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException(fieldName + " does not exist on " + target);
|
||||
}
|
||||
try {
|
||||
if (!field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
return field.get(target);
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalArgumentException("Error getting field " + fieldName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.xpath.*;
|
||||
|
||||
/**
|
||||
* Thread local XPath expression.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @since 3.4
|
||||
*/
|
||||
public class ThreadLocalXPathExpression extends ThreadLocal<XPathExpression> implements XPathExpression {
|
||||
|
||||
/** XPath expression */
|
||||
private final String expression;
|
||||
|
||||
/** Namespace context. */
|
||||
private final NamespaceContext context;
|
||||
|
||||
/**
|
||||
* Creates a new instance from an XPath expression and namespace context.
|
||||
*
|
||||
* @param xPath XPath expression.
|
||||
* @param context Namespace context for handling namespace prefix to URI mappings.
|
||||
*/
|
||||
public ThreadLocalXPathExpression(final String xPath, final NamespaceContext context) {
|
||||
this.expression = xPath;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(final Object o, final QName qName) throws XPathExpressionException {
|
||||
return get().evaluate(o, qName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String evaluate(final Object o) throws XPathExpressionException {
|
||||
return get().evaluate(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object evaluate(final InputSource inputSource, final QName qName) throws XPathExpressionException {
|
||||
return get().evaluate(inputSource, qName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String evaluate(final InputSource inputSource) throws XPathExpressionException {
|
||||
return get().evaluate(inputSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the XPath expression and returns the result coerced to a string.
|
||||
*
|
||||
* @param o Object on which to evaluate the expression; typically a DOM node.
|
||||
*
|
||||
* @return Evaluation result as a string.
|
||||
*
|
||||
* @throws XPathExpressionException On XPath evaluation errors.
|
||||
*/
|
||||
public String evaluateAsString(final Object o) throws XPathExpressionException {
|
||||
return (String) evaluate(o, XPathConstants.STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the XPath expression and returns the result coerced to a node list.
|
||||
*
|
||||
* @param o Object on which to evaluate the expression; typically a DOM node.
|
||||
*
|
||||
* @return Evaluation result as a node list.
|
||||
*
|
||||
* @throws XPathExpressionException On XPath evaluation errors.
|
||||
*/
|
||||
public NodeList evaluateAsNodeList(final Object o) throws XPathExpressionException {
|
||||
return (NodeList) evaluate(o, XPathConstants.NODESET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XPathExpression initialValue() {
|
||||
try {
|
||||
final XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
xPath.setNamespaceContext(context);
|
||||
return xPath.compile(expression);
|
||||
} catch (final XPathExpressionException e) {
|
||||
throw new IllegalArgumentException("Invalid XPath expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,682 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A utility class borrowed from apache http-client to build uris.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.4
|
||||
*/
|
||||
public final class URIBuilder {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(URIBuilder.class);
|
||||
private static final Pattern IPV6_STD_PATTERN = Pattern.compile("^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
|
||||
|
||||
private String scheme;
|
||||
private String encodedSchemeSpecificPart;
|
||||
private String encodedAuthority;
|
||||
private String userInfo;
|
||||
private String encodedUserInfo;
|
||||
private String host;
|
||||
private int port;
|
||||
private String path;
|
||||
private String encodedPath;
|
||||
private String encodedQuery;
|
||||
private List<BasicNameValuePair> queryParams;
|
||||
private String query;
|
||||
private boolean encode;
|
||||
private String fragment;
|
||||
private String encodedFragment;
|
||||
|
||||
/**
|
||||
* Constructs an empty instance.
|
||||
*/
|
||||
public URIBuilder() {
|
||||
super();
|
||||
this.port = -1;
|
||||
}
|
||||
|
||||
public URIBuilder(final boolean encode) {
|
||||
this();
|
||||
setEncode(encode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance from the string which must be a valid URI.
|
||||
*
|
||||
* @param string a valid URI in string form
|
||||
* @throws RuntimeException if the input is not a valid URI
|
||||
*/
|
||||
public URIBuilder(final String string) {
|
||||
super();
|
||||
try {
|
||||
digestURI(new URI(string));
|
||||
} catch (final URISyntaxException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public URIBuilder(final String string, final boolean encode) {
|
||||
super();
|
||||
try {
|
||||
setEncode(encode);
|
||||
digestURI(new URI(string));
|
||||
} catch (final URISyntaxException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct an instance from the provided URI.
|
||||
*
|
||||
* @param uri the uri to digest
|
||||
*/
|
||||
public URIBuilder(final URI uri) {
|
||||
super();
|
||||
digestURI(uri);
|
||||
}
|
||||
|
||||
private List<BasicNameValuePair> parseQuery(final String query) {
|
||||
|
||||
try {
|
||||
final Charset utf8 = Charset.forName("UTF-8");
|
||||
if (query != null && !query.isEmpty()) {
|
||||
final List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>();
|
||||
final String[] parametersArray = query.split("&");
|
||||
|
||||
for (final String parameter : parametersArray) {
|
||||
final int firstIndex = parameter.indexOf("=");
|
||||
if (firstIndex != -1) {
|
||||
final String paramName = parameter.substring(0, firstIndex);
|
||||
final String decodedParamName = URLDecoder.decode(paramName, utf8.name());
|
||||
|
||||
final String paramVal = parameter.substring(firstIndex + 1);
|
||||
final String decodedParamVal = URLDecoder.decode(paramVal, utf8.name());
|
||||
|
||||
list.add(new BasicNameValuePair(decodedParamName, decodedParamVal));
|
||||
} else {
|
||||
// Either we do not have a query parameter, or it might be encoded; take it verbaitm
|
||||
final String[] parameterCombo = parameter.split("=");
|
||||
if (parameterCombo.length >= 1) {
|
||||
final String key = URLDecoder.decode(parameterCombo[0], utf8.name());
|
||||
final String val = parameterCombo.length == 2 ? URLDecoder.decode(parameterCombo[1], utf8.name()) : "";
|
||||
list.add(new BasicNameValuePair(key, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
}
|
||||
return new ArrayList<BasicNameValuePair>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link URI} instance.
|
||||
*/
|
||||
public URI build() {
|
||||
try {
|
||||
return new URI(buildString());
|
||||
} catch (final URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isIPv6Address(final String input) {
|
||||
return IPV6_STD_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
private String buildString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (this.scheme != null) {
|
||||
sb.append(this.scheme).append(':');
|
||||
}
|
||||
if (this.encodedSchemeSpecificPart != null) {
|
||||
sb.append(this.encodedSchemeSpecificPart);
|
||||
} else {
|
||||
if (this.encodedAuthority != null) {
|
||||
sb.append("//").append(this.encodedAuthority);
|
||||
} else if (this.host != null) {
|
||||
sb.append("//");
|
||||
if (this.encodedUserInfo != null) {
|
||||
sb.append(this.encodedUserInfo).append("@");
|
||||
} else if (this.userInfo != null) {
|
||||
sb.append(encodeUserInfo(this.userInfo)).append("@");
|
||||
}
|
||||
if (isIPv6Address(this.host)) {
|
||||
sb.append("[").append(this.host).append("]");
|
||||
} else {
|
||||
sb.append(this.host);
|
||||
}
|
||||
if (this.port >= 0) {
|
||||
sb.append(":").append(this.port);
|
||||
}
|
||||
}
|
||||
if (this.encodedPath != null) {
|
||||
sb.append(normalizePath(this.encodedPath));
|
||||
} else if (this.path != null) {
|
||||
sb.append(encodePath(normalizePath(this.path)));
|
||||
}
|
||||
if (this.encodedQuery != null) {
|
||||
sb.append("?").append(this.encodedQuery);
|
||||
} else if (this.queryParams != null && !this.queryParams.isEmpty()) {
|
||||
sb.append("?").append(encodeUrlForm(this.queryParams));
|
||||
} else if (this.query != null) {
|
||||
sb.append("?").append(encodeUric(this.query));
|
||||
}
|
||||
}
|
||||
if (this.encodedFragment != null) {
|
||||
sb.append("#").append(this.encodedFragment);
|
||||
} else if (this.fragment != null) {
|
||||
sb.append("#").append(encodeUric(this.fragment));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public URIBuilder digestURI(final URI uri) {
|
||||
this.scheme = uri.getScheme();
|
||||
this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
|
||||
this.encodedAuthority = uri.getRawAuthority();
|
||||
this.host = uri.getHost();
|
||||
this.port = uri.getPort();
|
||||
this.encodedUserInfo = uri.getRawUserInfo();
|
||||
this.userInfo = uri.getUserInfo();
|
||||
this.encodedPath = uri.getRawPath();
|
||||
this.path = uri.getPath();
|
||||
this.encodedQuery = uri.getRawQuery();
|
||||
this.queryParams = parseQuery(uri.getRawQuery());
|
||||
this.encodedFragment = uri.getRawFragment();
|
||||
this.fragment = uri.getFragment();
|
||||
return this;
|
||||
}
|
||||
|
||||
private String encodeUserInfo(final String userInfo) {
|
||||
return this.encode ? CommonUtils.urlEncode(userInfo) : userInfo;
|
||||
}
|
||||
|
||||
private String encodePath(final String path) {
|
||||
return this.encode ? CommonUtils.urlEncode(path) : path;
|
||||
}
|
||||
|
||||
private String encodeUrlForm(final List<BasicNameValuePair> params) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (final BasicNameValuePair parameter : params) {
|
||||
final String encodedName = this.encode ? CommonUtils.urlEncode(parameter.getName()) : parameter.getName();
|
||||
final String encodedValue = this.encode ? CommonUtils.urlEncode(parameter.getValue()) : parameter.getValue();
|
||||
|
||||
if (result.length() > 0) {
|
||||
result.append("&");
|
||||
}
|
||||
result.append(encodedName);
|
||||
if (encodedValue != null) {
|
||||
result.append("=");
|
||||
result.append(encodedValue);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String encodeUric(final String fragment) {
|
||||
return this.encode ? CommonUtils.urlEncode(fragment) : fragment;
|
||||
}
|
||||
|
||||
public URIBuilder setEncode(final boolean encode) {
|
||||
this.encode = encode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI scheme.
|
||||
*/
|
||||
public URIBuilder setScheme(final String scheme) {
|
||||
this.scheme = scheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI user info. The value is expected to be unescaped and may contain non ASCII
|
||||
* characters.
|
||||
*/
|
||||
public URIBuilder setUserInfo(final String userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.encodedAuthority = null;
|
||||
this.encodedUserInfo = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI user info as a combination of username and password. These values are expected to
|
||||
* be unescaped and may contain non ASCII characters.
|
||||
*/
|
||||
public URIBuilder setUserInfo(final String username, final String password) {
|
||||
return setUserInfo(username + ':' + password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI host.
|
||||
*/
|
||||
public URIBuilder setHost(final String host) {
|
||||
this.host = host;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.encodedAuthority = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI port.
|
||||
*/
|
||||
public URIBuilder setPort(final int port) {
|
||||
this.port = port < 0 ? -1 : port;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.encodedAuthority = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
|
||||
*/
|
||||
public URIBuilder setPath(final String path) {
|
||||
this.path = path;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.encodedPath = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder setEncodedPath(final String path) {
|
||||
this.encodedPath = path;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes URI query.
|
||||
*/
|
||||
public URIBuilder removeQuery() {
|
||||
this.queryParams = null;
|
||||
this.query = null;
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI query parameters. The parameter name / values are expected to be unescaped
|
||||
* and may contain non ASCII characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove custom query if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder setParameters(final List<BasicNameValuePair> nvps) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
this.queryParams.addAll(nvps);
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder setParameters(final String queryParameters) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
this.queryParams.addAll(parseQuery(queryParameters));
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds URI query parameters. The parameter name / values are expected to be unescaped
|
||||
* and may contain non ASCII characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove custom query if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder addParameters(final List<BasicNameValuePair> nvps) {
|
||||
if (this.queryParams == null || this.queryParams.isEmpty()) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
}
|
||||
this.queryParams.addAll(nvps);
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI query parameters. The parameter name / values are expected to be unescaped
|
||||
* and may contain non ASCII characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove custom query if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder setParameters(final BasicNameValuePair... nvps) {
|
||||
if (this.queryParams == null) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
} else {
|
||||
this.queryParams.clear();
|
||||
}
|
||||
for (final BasicNameValuePair nvp : nvps) {
|
||||
this.queryParams.add(nvp);
|
||||
}
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameter to URI query. The parameter name and value are expected to be unescaped
|
||||
* and may contain non ASCII characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove custom query if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder addParameter(final String param, final String value) {
|
||||
if (this.queryParams == null) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
}
|
||||
this.queryParams.add(new BasicNameValuePair(param, value));
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets parameter of URI query overriding existing value if set. The parameter name and value
|
||||
* are expected to be unescaped and may contain non ASCII characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove custom query if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder setParameter(final String param, final String value) {
|
||||
if (this.queryParams == null) {
|
||||
this.queryParams = new ArrayList<BasicNameValuePair>();
|
||||
}
|
||||
if (!this.queryParams.isEmpty()) {
|
||||
for (final Iterator<BasicNameValuePair> it = this.queryParams.iterator(); it.hasNext(); ) {
|
||||
final BasicNameValuePair nvp = it.next();
|
||||
if (nvp.getName().equals(param)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.queryParams.add(new BasicNameValuePair(param, value));
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.query = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears URI query parameters.
|
||||
*/
|
||||
public URIBuilder clearParameters() {
|
||||
this.queryParams = null;
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom URI query. The value is expected to be unescaped and may contain non ASCII
|
||||
* characters.
|
||||
* <p>
|
||||
* Please note query parameters and custom query component are mutually exclusive. This method
|
||||
* will remove query parameters if present.
|
||||
* </p>
|
||||
*/
|
||||
public URIBuilder setCustomQuery(final String query) {
|
||||
this.query = query;
|
||||
this.encodedQuery = null;
|
||||
this.encodedSchemeSpecificPart = null;
|
||||
this.queryParams = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets URI fragment. The value is expected to be unescaped and may contain non ASCII
|
||||
* characters.
|
||||
*/
|
||||
public URIBuilder setFragment(final String fragment) {
|
||||
this.fragment = fragment;
|
||||
this.encodedFragment = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder setEncodedFragment(final String fragment) {
|
||||
this.fragment = null;
|
||||
this.encodedFragment = fragment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URIBuilder setEncodedQuery(final String query) {
|
||||
this.query = null;
|
||||
this.encodedFragment = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isAbsolute() {
|
||||
return this.scheme != null;
|
||||
}
|
||||
|
||||
public boolean isOpaque() {
|
||||
return this.path == null;
|
||||
}
|
||||
|
||||
public String getScheme() {
|
||||
return this.scheme;
|
||||
}
|
||||
|
||||
public String getUserInfo() {
|
||||
return this.userInfo;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public String getEncodedPath() {
|
||||
return this.encodedPath;
|
||||
}
|
||||
|
||||
public List<BasicNameValuePair> getQueryParams() {
|
||||
if (this.queryParams != null) {
|
||||
return new ArrayList<BasicNameValuePair>(this.queryParams);
|
||||
}
|
||||
return new ArrayList<BasicNameValuePair>();
|
||||
|
||||
}
|
||||
|
||||
public String getFragment() {
|
||||
return this.fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return buildString();
|
||||
}
|
||||
|
||||
private static String normalizePath(final String path) {
|
||||
String s = path;
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
int n = 0;
|
||||
for (; n < s.length(); n++) {
|
||||
if (s.charAt(n) != '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n > 1) {
|
||||
s = s.substring(n - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final URIBuilder that = (URIBuilder) o;
|
||||
|
||||
if (port != that.port) return false;
|
||||
if (encode != that.encode) return false;
|
||||
if (scheme != null ? !scheme.equals(that.scheme) : that.scheme != null) return false;
|
||||
if (encodedSchemeSpecificPart != null ? !encodedSchemeSpecificPart.equals(that.encodedSchemeSpecificPart) : that.encodedSchemeSpecificPart != null)
|
||||
return false;
|
||||
if (encodedAuthority != null ? !encodedAuthority.equals(that.encodedAuthority) : that.encodedAuthority != null)
|
||||
return false;
|
||||
if (userInfo != null ? !userInfo.equals(that.userInfo) : that.userInfo != null) return false;
|
||||
if (encodedUserInfo != null ? !encodedUserInfo.equals(that.encodedUserInfo) : that.encodedUserInfo != null)
|
||||
return false;
|
||||
if (host != null ? !host.equals(that.host) : that.host != null) return false;
|
||||
if (path != null ? !path.equals(that.path) : that.path != null) return false;
|
||||
if (encodedPath != null ? !encodedPath.equals(that.encodedPath) : that.encodedPath != null) return false;
|
||||
if (encodedQuery != null ? !encodedQuery.equals(that.encodedQuery) : that.encodedQuery != null) return false;
|
||||
if (queryParams != null ? !queryParams.equals(that.queryParams) : that.queryParams != null) return false;
|
||||
if (query != null ? !query.equals(that.query) : that.query != null) return false;
|
||||
if (fragment != null ? !fragment.equals(that.fragment) : that.fragment != null) return false;
|
||||
return !(encodedFragment != null ? !encodedFragment.equals(that.encodedFragment) : that.encodedFragment != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = scheme != null ? scheme.hashCode() : 0;
|
||||
result = 31 * result + (encodedSchemeSpecificPart != null ? encodedSchemeSpecificPart.hashCode() : 0);
|
||||
result = 31 * result + (encodedAuthority != null ? encodedAuthority.hashCode() : 0);
|
||||
result = 31 * result + (userInfo != null ? userInfo.hashCode() : 0);
|
||||
result = 31 * result + (encodedUserInfo != null ? encodedUserInfo.hashCode() : 0);
|
||||
result = 31 * result + (host != null ? host.hashCode() : 0);
|
||||
result = 31 * result + port;
|
||||
result = 31 * result + (path != null ? path.hashCode() : 0);
|
||||
result = 31 * result + (encodedPath != null ? encodedPath.hashCode() : 0);
|
||||
result = 31 * result + (encodedQuery != null ? encodedQuery.hashCode() : 0);
|
||||
result = 31 * result + (queryParams != null ? queryParams.hashCode() : 0);
|
||||
result = 31 * result + (query != null ? query.hashCode() : 0);
|
||||
result = 31 * result + (encode ? 1 : 0);
|
||||
result = 31 * result + (fragment != null ? fragment.hashCode() : 0);
|
||||
result = 31 * result + (encodedFragment != null ? encodedFragment.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class BasicNameValuePair implements Cloneable, Serializable {
|
||||
private static final long serialVersionUID = -6437800749411518984L;
|
||||
|
||||
private final String name;
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Default Constructor taking a name and a value. The value may be null.
|
||||
*
|
||||
* @param name The name.
|
||||
* @param value The value.
|
||||
*/
|
||||
public BasicNameValuePair(final String name, final String value) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// don't call complex default formatting for a simple toString
|
||||
|
||||
if (this.value == null) {
|
||||
return name;
|
||||
}
|
||||
final int len = this.name.length() + 1 + this.value.length();
|
||||
final StringBuilder buffer = new StringBuilder(len);
|
||||
buffer.append(this.name);
|
||||
buffer.append("=");
|
||||
buffer.append(this.value);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object instanceof BasicNameValuePair) {
|
||||
final BasicNameValuePair that = (BasicNameValuePair) object;
|
||||
return this.name.equals(that.name)
|
||||
&& this.value.equals(that.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 133 * this.name.hashCode() * this.value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,44 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import org.xml.sax.helpers.XMLReaderFactory;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* Common utilities for easily parsing XML without duplicating logic.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class XmlUtils {
|
||||
|
|
@ -30,7 +46,37 @@ public final class XmlUtils {
|
|||
/**
|
||||
* Static instance of Commons Logging.
|
||||
*/
|
||||
private final static Log LOG = LogFactory.getLog(XmlUtils.class);
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(XmlUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new namespace-aware DOM document object by parsing the given XML.
|
||||
*
|
||||
* @param xml XML content.
|
||||
*
|
||||
* @return DOM document.
|
||||
*/
|
||||
public static Document newDocument(final String xml) {
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
final Map<String, Boolean> features = new HashMap<String, Boolean>();
|
||||
features.put(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
features.put("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
for (final Map.Entry<String, Boolean> entry : features.entrySet()) {
|
||||
try {
|
||||
factory.setFeature(entry.getKey(), entry.getValue());
|
||||
} catch (final ParserConfigurationException e) {
|
||||
LOGGER.warn("Failed setting XML feature {}: {}", entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
factory.setExpandEntityReferences(false);
|
||||
factory.setNamespaceAware(true);
|
||||
try {
|
||||
return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("XML parsing error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of an XML reader from the XMLReaderFactory.
|
||||
|
|
@ -39,12 +85,20 @@ public final class XmlUtils {
|
|||
*/
|
||||
public static XMLReader getXmlReader() {
|
||||
try {
|
||||
return XMLReaderFactory.createXMLReader();
|
||||
} catch (final SAXException e) {
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
return factory.newSAXParser().getXMLReader();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Unable to create XMLReader", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the text for a group of elements. Each text element is an entry
|
||||
* in a list.
|
||||
|
|
@ -54,36 +108,35 @@ public final class XmlUtils {
|
|||
* @param element the element to look for
|
||||
* @return the list of text from the elements.
|
||||
*/
|
||||
public static List getTextForElements(final String xmlAsString,
|
||||
final String element) {
|
||||
final List elements = new ArrayList(2);
|
||||
public static List<String> getTextForElements(final String xmlAsString, final String element) {
|
||||
final List<String> elements = new ArrayList<String>(2);
|
||||
final XMLReader reader = getXmlReader();
|
||||
|
||||
final DefaultHandler handler = new DefaultHandler() {
|
||||
|
||||
private boolean foundElement = false;
|
||||
|
||||
private StringBuffer buffer = new StringBuffer();
|
||||
private StringBuilder buffer = new StringBuilder();
|
||||
|
||||
public void startElement(final String uri, final String localName,
|
||||
final String qName, final Attributes attributes)
|
||||
throws SAXException {
|
||||
@Override
|
||||
public void startElement(final String uri, final String localName, final String qName,
|
||||
final Attributes attributes) throws SAXException {
|
||||
if (localName.equals(element)) {
|
||||
this.foundElement = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void endElement(final String uri, final String localName,
|
||||
final String qName) throws SAXException {
|
||||
@Override
|
||||
public void endElement(final String uri, final String localName, final String qName) throws SAXException {
|
||||
if (localName.equals(element)) {
|
||||
this.foundElement = false;
|
||||
elements.add(this.buffer.toString());
|
||||
this.buffer = new StringBuffer();
|
||||
this.buffer = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
public void characters(char[] ch, int start, int length)
|
||||
throws SAXException {
|
||||
@Override
|
||||
public void characters(final char[] ch, final int start, final int length) throws SAXException {
|
||||
if (this.foundElement) {
|
||||
this.buffer.append(ch, start, length);
|
||||
}
|
||||
|
|
@ -96,7 +149,7 @@ public final class XmlUtils {
|
|||
try {
|
||||
reader.parse(new InputSource(new StringReader(xmlAsString)));
|
||||
} catch (final Exception e) {
|
||||
LOG.error(e, e);
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -111,34 +164,33 @@ public final class XmlUtils {
|
|||
* @param element the element to look for
|
||||
* @return the text value of the element.
|
||||
*/
|
||||
public static String getTextForElement(final String xmlAsString,
|
||||
final String element) {
|
||||
public static String getTextForElement(final String xmlAsString, final String element) {
|
||||
final XMLReader reader = getXmlReader();
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
final DefaultHandler handler = new DefaultHandler() {
|
||||
|
||||
private boolean foundElement = false;
|
||||
|
||||
public void startElement(final String uri, final String localName,
|
||||
final String qName, final Attributes attributes)
|
||||
throws SAXException {
|
||||
@Override
|
||||
public void startElement(final String uri, final String localName, final String qName,
|
||||
final Attributes attributes) throws SAXException {
|
||||
if (localName.equals(element)) {
|
||||
this.foundElement = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void endElement(final String uri, final String localName,
|
||||
final String qName) throws SAXException {
|
||||
@Override
|
||||
public void endElement(final String uri, final String localName, final String qName) throws SAXException {
|
||||
if (localName.equals(element)) {
|
||||
this.foundElement = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void characters(char[] ch, int start, int length)
|
||||
throws SAXException {
|
||||
@Override
|
||||
public void characters(final char[] ch, final int start, final int length) throws SAXException {
|
||||
if (this.foundElement) {
|
||||
buffer.append(ch, start, length);
|
||||
builder.append(ch, start, length);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -149,10 +201,10 @@ public final class XmlUtils {
|
|||
try {
|
||||
reader.parse(new InputSource(new StringReader(xmlAsString)));
|
||||
} catch (final Exception e) {
|
||||
LOG.error(e, e);
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,23 @@
|
|||
<!--
|
||||
|
||||
Licensed to Apereo under one or more contributor license
|
||||
agreements. See the NOTICE file distributed with this work
|
||||
for additional information regarding copyright ownership.
|
||||
Apereo licenses this file to you under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a
|
||||
copy of the License at the following location:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
<p>The validation package includes interfaces for validating Tickets, as well as the common implementations.</p>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Abstract class that knows the protocol for validating a CAS ticket.
|
||||
|
|
@ -27,31 +37,8 @@ public abstract class AbstractCasProtocolUrlBasedTicketValidator extends Abstrac
|
|||
/**
|
||||
* Retrieves the response from the server by opening a connection and merely reading the response.
|
||||
*/
|
||||
@Override
|
||||
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
|
||||
HttpURLConnection connection = null;
|
||||
|
||||
try {
|
||||
connection = (HttpURLConnection) validationUrl.openConnection();
|
||||
final BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
|
||||
String line;
|
||||
final StringBuffer stringBuffer = new StringBuffer(255);
|
||||
|
||||
synchronized (stringBuffer) {
|
||||
while ((line = in.readLine()) != null) {
|
||||
stringBuffer.append(line);
|
||||
stringBuffer.append("\n");
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error(e,e);
|
||||
return null;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,49 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
* The filter that handles all the work of validating ticket requests.
|
||||
* <p>
|
||||
* This filter can be configured with the following values:
|
||||
* <ul>
|
||||
* <li><code>redirectAfterValidation</code> - redirect the CAS client to the same URL without the ticket.</li>
|
||||
* <li><code>redirectAfterValidation</code> - redirect the CAS client to the same URL without the ticket.
|
||||
* (default: true, Will be forced to false when {@link #useSession} is false.)</li>
|
||||
* <li><code>exceptionOnValidationFailure</code> - throw an exception if the validation fails. Otherwise, continue
|
||||
* processing.</li>
|
||||
* <li><code>useSession</code> - store any of the useful information in a session attribute.</li>
|
||||
* processing. (default: true)</li>
|
||||
* <li><code>useSession</code> - store any of the useful information in a session attribute. (default: true)</li>
|
||||
* <li><code>hostnameVerifier</code> - name of class implementing a {@link HostnameVerifier}.</li>
|
||||
* <li><code>hostnameVerifierConfig</code> - name of configuration class (constructor argument of verifier).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
|
|
@ -42,31 +60,89 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
* successful validation to remove the ticket parameter from the query
|
||||
* string.
|
||||
*/
|
||||
private boolean redirectAfterValidation = false;
|
||||
private boolean redirectAfterValidation = true;
|
||||
|
||||
/** Determines whether an exception is thrown when there is a ticket validation failure. */
|
||||
private boolean exceptionOnValidationFailure = true;
|
||||
private boolean exceptionOnValidationFailure = false;
|
||||
|
||||
/**
|
||||
* Specify whether the Assertion should be stored in a session
|
||||
* attribute {@link AbstractCasFilter#CONST_CAS_ASSERTION}.
|
||||
*/
|
||||
private boolean useSession = true;
|
||||
|
||||
protected AbstractTicketValidationFilter(final Protocol protocol) {
|
||||
super(protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method to return the appropriate validator.
|
||||
*
|
||||
* @param filterConfig the FilterConfiguration that may be needed to construct a validator.
|
||||
* @return the ticket validator.
|
||||
*/
|
||||
protected TicketValidator getTicketValidator(FilterConfig filterConfig) {
|
||||
protected TicketValidator getTicketValidator(final FilterConfig filterConfig) {
|
||||
return this.ticketValidator;
|
||||
}
|
||||
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
super.initInternal(filterConfig);
|
||||
setExceptionOnValidationFailure(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true")));
|
||||
setRedirectAfterValidation(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "false")));
|
||||
setUseSession(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
|
||||
setTicketValidator(getTicketValidator(filterConfig));
|
||||
/**
|
||||
* Gets the ssl config to use for HTTPS connections
|
||||
* if one is configured for this filter.
|
||||
* @return Properties that can contains key/trust info for Client Side Certificates
|
||||
*/
|
||||
protected Properties getSSLConfig() {
|
||||
final Properties properties = new Properties();
|
||||
final String fileName = getString(ConfigurationKeys.SSL_CONFIG_FILE);
|
||||
|
||||
if (fileName != null) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(fileName);
|
||||
properties.load(fis);
|
||||
logger.trace("Loaded {} entries from {}", properties.size(), fileName);
|
||||
} catch (final IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(fis);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured {@link HostnameVerifier} to use for HTTPS connections
|
||||
* if one is configured for this filter.
|
||||
* @return Instance of specified host name verifier or null if none specified.
|
||||
*/
|
||||
protected HostnameVerifier getHostnameVerifier() {
|
||||
final Class<? extends HostnameVerifier> className = getClass(ConfigurationKeys.HOSTNAME_VERIFIER);
|
||||
final String config = getString(ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG);
|
||||
if (className != null) {
|
||||
if (config != null) {
|
||||
return ReflectUtils.newInstance(className, config);
|
||||
} else {
|
||||
return ReflectUtils.newInstance(className);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
setExceptionOnValidationFailure(getBoolean(ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE));
|
||||
setRedirectAfterValidation(getBoolean(ConfigurationKeys.REDIRECT_AFTER_VALIDATION));
|
||||
setUseSession(getBoolean(ConfigurationKeys.USE_SESSION));
|
||||
|
||||
if (!this.useSession && this.redirectAfterValidation) {
|
||||
logger.warn("redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects.");
|
||||
setRedirectAfterValidation(false);
|
||||
}
|
||||
|
||||
setTicketValidator(getTicketValidator(filterConfig));
|
||||
super.initInternal(filterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
|
||||
|
|
@ -82,7 +158,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
* @throws IOException if there is an I/O problem
|
||||
* @throws ServletException if there is a servlet problem.
|
||||
*/
|
||||
protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +172,9 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
* @param response the HttpServletResponse.
|
||||
* @param assertion the successful Assertion from the server.
|
||||
*/
|
||||
protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, final Assertion assertion) {
|
||||
// nothing to do here.
|
||||
protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response,
|
||||
final Assertion assertion) {
|
||||
// nothing to do here.
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -110,7 +188,9 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
// nothing to do here.
|
||||
}
|
||||
|
||||
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
@Override
|
||||
public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
|
||||
if (!preFilter(servletRequest, servletResponse, filterChain)) {
|
||||
return;
|
||||
|
|
@ -118,44 +198,40 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
final String ticket = request.getParameter(getArtifactParameterName());
|
||||
final String ticket = retrieveTicketFromRequest(request);
|
||||
|
||||
if (CommonUtils.isNotBlank(ticket)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Attempting to validate ticket: " + ticket);
|
||||
}
|
||||
logger.debug("Attempting to validate ticket: {}", ticket);
|
||||
|
||||
try {
|
||||
final Assertion assertion = this.ticketValidator.validate(
|
||||
ticket, constructServiceUrl(request,
|
||||
response));
|
||||
final Assertion assertion = this.ticketValidator.validate(ticket,
|
||||
constructServiceUrl(request, response));
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Successfully authenticated user: "
|
||||
+ assertion.getPrincipal().getName());
|
||||
}
|
||||
logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
|
||||
|
||||
request.setAttribute(CONST_CAS_ASSERTION, assertion);
|
||||
|
||||
if (this.useSession) {
|
||||
request.getSession().setAttribute(CONST_CAS_ASSERTION,
|
||||
assertion);
|
||||
request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);
|
||||
}
|
||||
onSuccessfulValidation(request, response, assertion);
|
||||
|
||||
if (this.redirectAfterValidation) {
|
||||
logger.debug("Redirecting after successful ticket validation.");
|
||||
response.sendRedirect(constructServiceUrl(request, response));
|
||||
return;
|
||||
}
|
||||
} catch (final TicketValidationException e) {
|
||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||
log.warn(e, e);
|
||||
logger.debug(e.getMessage(), e);
|
||||
|
||||
onFailedValidation(request, response);
|
||||
|
||||
if (this.exceptionOnValidationFailure) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.redirectAfterValidation) {
|
||||
response.sendRedirect(response
|
||||
.encodeRedirectURL(constructServiceUrl(request, response)));
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -165,8 +241,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
}
|
||||
|
||||
public final void setTicketValidator(final TicketValidator ticketValidator) {
|
||||
this.ticketValidator = ticketValidator;
|
||||
}
|
||||
this.ticketValidator = ticketValidator;
|
||||
}
|
||||
|
||||
public final void setRedirectAfterValidation(final boolean redirectAfterValidation) {
|
||||
this.redirectAfterValidation = redirectAfterValidation;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,50 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Abstract validator implementation for tickets that must be validated against a server.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractUrlBasedTicketValidator implements TicketValidator {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Commons Logging instance.
|
||||
* URLConnection factory instance to use when making validation requests to the CAS server.
|
||||
* Defaults to {@link HttpsURLConnectionFactory}
|
||||
*/
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
private HttpURLConnectionFactory urlConnectionFactory = new HttpsURLConnectionFactory();
|
||||
|
||||
/**
|
||||
* Prefix for the CAS server. Should be everything up to the url endpoint, including the /.
|
||||
|
|
@ -43,14 +58,21 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
*/
|
||||
private boolean renew;
|
||||
|
||||
/**
|
||||
* A map containing custom parameters to pass to the validation url.
|
||||
*/
|
||||
private Map<String, String> customParameters;
|
||||
|
||||
private String encoding;
|
||||
|
||||
/**
|
||||
* Constructs a new TicketValidator with the casServerUrlPrefix.
|
||||
*
|
||||
* @param casServerUrlPrefix the location of the CAS server.
|
||||
*/
|
||||
protected AbstractUrlBasedTicketValidator(final String casServerUrlPrefix) {
|
||||
this.casServerUrlPrefix = casServerUrlPrefix;
|
||||
CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
|
||||
CommonUtils.assertNotNull(casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
|
||||
this.casServerUrlPrefix = CommonUtils.addTrailingSlash(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -58,7 +80,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
*
|
||||
* @param urlParameters the map containing the parameters.
|
||||
*/
|
||||
protected void populateUrlAttributeMap(final Map urlParameters) {
|
||||
protected void populateUrlAttributeMap(final Map<String, String> urlParameters) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +90,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
*/
|
||||
protected abstract String getUrlSuffix();
|
||||
|
||||
|
||||
/**
|
||||
* Constructs the URL to send the validation request to.
|
||||
*
|
||||
|
|
@ -76,41 +99,48 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
* @return the fully constructed URL.
|
||||
*/
|
||||
protected final String constructValidationUrl(final String ticket, final String serviceUrl) {
|
||||
final Map urlParameters = new HashMap();
|
||||
final Map<String, String> urlParameters = new HashMap<String, String>();
|
||||
|
||||
logger.debug("Placing URL parameters in map.");
|
||||
urlParameters.put("ticket", ticket);
|
||||
urlParameters.put("service", encodeUrl(serviceUrl));
|
||||
urlParameters.put("service", serviceUrl);
|
||||
|
||||
if (this.renew) {
|
||||
urlParameters.put("renew", "true");
|
||||
}
|
||||
|
||||
logger.debug("Calling template URL attribute map.");
|
||||
populateUrlAttributeMap(urlParameters);
|
||||
|
||||
logger.debug("Loading custom parameters from configuration.");
|
||||
if (this.customParameters != null) {
|
||||
urlParameters.putAll(this.customParameters);
|
||||
}
|
||||
|
||||
final String suffix = getUrlSuffix();
|
||||
final StringBuffer buffer = new StringBuffer(urlParameters.size()*10 + this.casServerUrlPrefix.length() + suffix.length() +1);
|
||||
final StringBuilder buffer = new StringBuilder(urlParameters.size() * 10 + this.casServerUrlPrefix.length()
|
||||
+ suffix.length() + 1);
|
||||
|
||||
int i = 0;
|
||||
synchronized (buffer) {
|
||||
buffer.append(this.casServerUrlPrefix);
|
||||
buffer.append("/");
|
||||
buffer.append(suffix);
|
||||
|
||||
for (final Iterator iter = urlParameters.entrySet().iterator(); iter.hasNext();) {
|
||||
buffer.append(this.casServerUrlPrefix);
|
||||
buffer.append(suffix);
|
||||
|
||||
for (final Map.Entry<String, String> entry : urlParameters.entrySet()) {
|
||||
final String key = entry.getKey();
|
||||
final String value = entry.getValue();
|
||||
|
||||
if (value != null) {
|
||||
buffer.append(i++ == 0 ? "?" : "&");
|
||||
final Map.Entry entry = (Map.Entry) iter.next();
|
||||
final String key = (String) entry.getKey();
|
||||
final String value = (String) entry.getValue();
|
||||
|
||||
if (value != null) {
|
||||
buffer.append(key);
|
||||
buffer.append("=");
|
||||
buffer.append(value);
|
||||
}
|
||||
buffer.append(key);
|
||||
buffer.append("=");
|
||||
final String encodedValue = encodeUrl(value);
|
||||
buffer.append(encodedValue);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -120,10 +150,10 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
* @return the encoded url, or the original url if "UTF-8" character encoding could not be found.
|
||||
*/
|
||||
protected final String encodeUrl(final String url) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return URLEncoder.encode(url, "UTF-8");
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
|
|
@ -151,24 +181,60 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
|
||||
protected abstract String retrieveResponseFromServer(URL validationUrl, String ticket);
|
||||
|
||||
public Assertion validate(final String ticket, final String service) throws TicketValidationException {
|
||||
|
||||
@Override
|
||||
public final Assertion validate(final String ticket, final String service) throws TicketValidationException {
|
||||
final String validationUrl = constructValidationUrl(ticket, service);
|
||||
logger.debug("Constructing validation url: {}", validationUrl);
|
||||
|
||||
try {
|
||||
logger.debug("Retrieving response from server.");
|
||||
final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);
|
||||
|
||||
if (serverResponse == null) {
|
||||
throw new TicketValidationException("The CAS server returned no response.");
|
||||
}
|
||||
|
||||
logger.debug("Server response: {}", serverResponse);
|
||||
|
||||
return parseResponseFromServer(serverResponse);
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new TicketValidationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenew(final boolean renew) {
|
||||
public final void setRenew(final boolean renew) {
|
||||
this.renew = renew;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setCustomParameters(final Map<String, String> customParameters) {
|
||||
this.customParameters = customParameters;
|
||||
}
|
||||
|
||||
public final void setEncoding(final String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
protected final String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
protected final boolean isRenew() {
|
||||
return this.renew;
|
||||
}
|
||||
|
||||
protected final String getCasServerUrlPrefix() {
|
||||
return this.casServerUrlPrefix;
|
||||
}
|
||||
|
||||
protected final Map<String, String> getCustomParameters() {
|
||||
return this.customParameters;
|
||||
}
|
||||
|
||||
protected HttpURLConnectionFactory getURLConnectionFactory() {
|
||||
return this.urlConnectionFactory;
|
||||
}
|
||||
|
||||
public void setURLConnectionFactory(final HttpURLConnectionFactory urlConnectionFactory) {
|
||||
this.urlConnectionFactory = urlConnectionFactory;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
|
||||
/**
|
||||
* Represents a response to a validation request.
|
||||
|
|
@ -34,12 +46,20 @@ public interface Assertion extends Serializable {
|
|||
*/
|
||||
Date getValidUntilDate();
|
||||
|
||||
/**
|
||||
* The date the authentication actually occurred on. If its unable to be determined, it should be set to the current
|
||||
* time.
|
||||
*
|
||||
* @return the authentication date, or the current time if it can't be determined.
|
||||
*/
|
||||
Date getAuthenticationDate();
|
||||
|
||||
/**
|
||||
* The key/value pairs associated with this assertion.
|
||||
*
|
||||
* @return the map of attributes.
|
||||
*/
|
||||
Map getAttributes();
|
||||
Map<String, Object> getAttributes();
|
||||
|
||||
/**
|
||||
* The principal for which this assertion is valid.
|
||||
|
|
@ -47,4 +67,12 @@ public interface Assertion extends Serializable {
|
|||
* @return the principal.
|
||||
*/
|
||||
AttributePrincipal getPrincipal();
|
||||
|
||||
/**
|
||||
* Determines whether an Assertion is considered usable or not. A naive implementation may just check the date validity.
|
||||
*
|
||||
* @return true if its valid, false otherwise.
|
||||
* @since 3.3.0 (though in 3.3.0, no one actually calls this)
|
||||
*/
|
||||
boolean isValid();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,30 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Concrete Implementation of the {@link Assertion}.
|
||||
*
|
||||
|
|
@ -24,16 +36,18 @@ import java.util.Map;
|
|||
public final class AssertionImpl implements Assertion {
|
||||
|
||||
/** Unique Id for serialization. */
|
||||
private static final long serialVersionUID = -7767943925833639221L;
|
||||
private static final long serialVersionUID = -7767943925833639221L;
|
||||
|
||||
/** The date from which the assertion is valid. */
|
||||
/** The date from which the assertion is valid. */
|
||||
private final Date validFromDate;
|
||||
|
||||
/** The date the assertion is valid until. */
|
||||
private final Date validUntilDate;
|
||||
|
||||
private final Date authenticationDate;
|
||||
|
||||
/** Map of key/value pairs associated with this assertion. I.e. authentication type. */
|
||||
private final Map attributes;
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
/** The principal for which this assertion is valid for. */
|
||||
private final AttributePrincipal principal;
|
||||
|
|
@ -44,16 +58,16 @@ public final class AssertionImpl implements Assertion {
|
|||
* @param name the name of the principal for which this assertion is valid.
|
||||
*/
|
||||
public AssertionImpl(final String name) {
|
||||
this(new AttributePrincipalImpl(name));
|
||||
this(new AttributePrincipalImpl(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Assrtion with the supplied Principal.
|
||||
* Creates a new Assertion with the supplied Principal.
|
||||
*
|
||||
* @param principal the Principal to associate with the Assertion.
|
||||
*/
|
||||
public AssertionImpl(final AttributePrincipal principal) {
|
||||
this(principal, new HashMap());
|
||||
this(principal, Collections.<String, Object> emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -62,41 +76,64 @@ public final class AssertionImpl implements Assertion {
|
|||
* @param principal the Principal to associate with the Assertion.
|
||||
* @param attributes the key/value pairs for this attribute.
|
||||
*/
|
||||
public AssertionImpl(final AttributePrincipal principal, final Map attributes) {
|
||||
this(principal, new Date(), null, attributes);
|
||||
public AssertionImpl(final AttributePrincipal principal, final Map<String, Object> attributes) {
|
||||
this(principal, new Date(), null, new Date(), attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creats a new Assertion with the supplied principal, Assertion attributes, and start and valid until dates.
|
||||
* Creates a new Assertion with the supplied principal, Assertion attributes, and start and valid until dates.
|
||||
*
|
||||
* @param principal the Principal to associate with the Assertion.
|
||||
* @param validFromDate when the assertion is valid from.
|
||||
* @param validUntilDate when the assertion is valid to.
|
||||
* @param attributes the key/value pairs for this attribute.
|
||||
*/
|
||||
public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate, final Map attributes) {
|
||||
public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate,
|
||||
final Date authenticationDate, final Map<String, Object> attributes) {
|
||||
this.principal = principal;
|
||||
this.validFromDate = validFromDate;
|
||||
this.validUntilDate = validUntilDate;
|
||||
this.attributes = attributes;
|
||||
this.authenticationDate = authenticationDate;
|
||||
|
||||
CommonUtils.assertNotNull(this.principal, "principal cannot be null.");
|
||||
CommonUtils.assertNotNull(this.validFromDate, "validFromDate cannot be null.");
|
||||
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getAuthenticationDate() {
|
||||
return this.authenticationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getValidFromDate() {
|
||||
return this.validFromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getValidUntilDate() {
|
||||
return this.validUntilDate;
|
||||
}
|
||||
|
||||
public Map getAttributes() {
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributePrincipal getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
if (this.validFromDate == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Date now = new Date();
|
||||
return (this.validFromDate.before(now) || this.validFromDate.equals(now))
|
||||
&& (this.validUntilDate == null || this.validUntilDate.after(now) || this.validUntilDate.equals(now));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,55 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
|
||||
/**
|
||||
* Implementation of AbstractTicketValidatorFilter that instanciates a Cas10TicketValidator.
|
||||
* Implementation of AbstractTicketValidatorFilter that creates a Cas10TicketValidator.
|
||||
* <p>Deployers can provide the "casServerPrefix" and the "renew" attributes via the standard context or filter init
|
||||
* parameters.
|
||||
*
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter {
|
||||
|
||||
public Cas10TicketValidationFilter() {
|
||||
super(Protocol.CAS1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
|
||||
final String casUrlServerPrefix = getPropertyFromInitParams(filterConfig, "casUrlServerPrefix", null);
|
||||
final Cas10TicketValidator validator = new Cas10TicketValidator(casUrlServerPrefix);
|
||||
validator.setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
|
||||
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
|
||||
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
|
||||
|
||||
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(),
|
||||
getSSLConfig());
|
||||
validator.setURLConnectionFactory(factory);
|
||||
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
|
|
@ -22,18 +35,19 @@ public final class Cas10TicketValidator extends AbstractCasProtocolUrlBasedTicke
|
|||
super(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlSuffix() {
|
||||
return "validate";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
|
||||
if (!response.startsWith("yes")) {
|
||||
throw new TicketValidationException("CAS Server could not validate ticket.");
|
||||
}
|
||||
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new StringReader(
|
||||
response));
|
||||
final BufferedReader reader = new BufferedReader(new StringReader(response));
|
||||
reader.readLine();
|
||||
final String name = reader.readLine();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,66 +1,157 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.*;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
import org.jasig.cas.client.configuration.ConfigurationKeys;
|
||||
import org.jasig.cas.client.proxy.*;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.PrivateKeyUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
|
||||
import static org.jasig.cas.client.configuration.ConfigurationKeys.*;
|
||||
|
||||
/**
|
||||
* Creates either a CAS20ProxyTicketValidator or a CAS20ServiceTicketValidator depending on whether any of the
|
||||
* proxy parameters are set.
|
||||
* <p/>
|
||||
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
|
||||
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @author Brad Cupit (brad [at] lsu {dot} edu)
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketValidationFilter {
|
||||
|
||||
/**
|
||||
* Constant representing the ProxyGrantingTicket IOU Request Parameter.
|
||||
*/
|
||||
private static final String PARAM_PROXY_GRANTING_TICKET_IOU = "pgtIou";
|
||||
private static final String[] RESERVED_INIT_PARAMS = new String[]{ARTIFACT_PARAMETER_NAME.getName(), SERVER_NAME.getName(), SERVICE.getName(), RENEW.getName(), LOGOUT_PARAMETER_NAME.getName(),
|
||||
ARTIFACT_PARAMETER_OVER_POST.getName(), EAGERLY_CREATE_SESSIONS.getName(), ENCODE_SERVICE_URL.getName(), SSL_CONFIG_FILE.getName(), ROLE_ATTRIBUTE.getName(), IGNORE_CASE.getName(),
|
||||
CAS_SERVER_LOGIN_URL.getName(), GATEWAY.getName(), AUTHENTICATION_REDIRECT_STRATEGY_CLASS.getName(), GATEWAY_STORAGE_CLASS.getName(), CAS_SERVER_URL_PREFIX.getName(), ENCODING.getName(),
|
||||
TOLERANCE.getName(), IGNORE_PATTERN.getName(), IGNORE_URL_PATTERN_TYPE.getName(), HOSTNAME_VERIFIER.getName(), HOSTNAME_VERIFIER_CONFIG.getName(),
|
||||
EXCEPTION_ON_VALIDATION_FAILURE.getName(), REDIRECT_AFTER_VALIDATION.getName(), USE_SESSION.getName(), SECRET_KEY.getName(), CIPHER_ALGORITHM.getName(), PROXY_RECEPTOR_URL.getName(),
|
||||
PROXY_GRANTING_TICKET_STORAGE_CLASS.getName(), MILLIS_BETWEEN_CLEAN_UPS.getName(), ACCEPT_ANY_PROXY.getName(), ALLOWED_PROXY_CHAINS.getName(), TICKET_VALIDATOR_CLASS.getName(),
|
||||
PROXY_CALLBACK_URL.getName(), RELAY_STATE_PARAMETER_NAME.getName(), METHOD.getName(), PRIVATE_KEY_PATH.getName(), PRIVATE_KEY_ALGORITHM.getName()
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant representing the ProxyGrantingTicket Request Parameter.
|
||||
*/
|
||||
private static final String PARAM_PROXY_GRANTING_TICKET = "pgtId";
|
||||
|
||||
/**
|
||||
* The URL to send to the CAS server as the URL that will process proxying requests on the CAS client.
|
||||
* The URL to send to the CAS server as the URL that will process proxying requests on the CAS client.
|
||||
*/
|
||||
private String proxyReceptorUrl;
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private TimerTask timerTask;
|
||||
|
||||
private int millisBetweenCleanUps;
|
||||
|
||||
protected Class<? extends Cas20ServiceTicketValidator> defaultServiceTicketValidatorClass;
|
||||
|
||||
protected Class<? extends Cas20ProxyTicketValidator> defaultProxyTicketValidatorClass;
|
||||
|
||||
private PrivateKey privateKey;
|
||||
|
||||
/**
|
||||
* Storage location of ProxyGrantingTickets and Proxy Ticket IOUs.
|
||||
*/
|
||||
private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
|
||||
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
super.initInternal(filterConfig);
|
||||
setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null));
|
||||
public Cas20ProxyReceivingTicketValidationFilter() {
|
||||
this(Protocol.CAS2);
|
||||
this.defaultServiceTicketValidatorClass = Cas20ServiceTicketValidator.class;
|
||||
this.defaultProxyTicketValidatorClass = Cas20ProxyTicketValidator.class;
|
||||
}
|
||||
|
||||
protected Cas20ProxyReceivingTicketValidationFilter(final Protocol protocol) {
|
||||
super(protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
|
||||
setProxyReceptorUrl(getString(ConfigurationKeys.PROXY_RECEPTOR_URL));
|
||||
|
||||
final Class<? extends ProxyGrantingTicketStorage> proxyGrantingTicketStorageClass = getClass(ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS);
|
||||
|
||||
if (proxyGrantingTicketStorageClass != null) {
|
||||
this.proxyGrantingTicketStorage = ReflectUtils.newInstance(proxyGrantingTicketStorageClass);
|
||||
|
||||
if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
|
||||
final AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
|
||||
final String cipherAlgorithm = getString(ConfigurationKeys.CIPHER_ALGORITHM);
|
||||
final String secretKey = getString(ConfigurationKeys.SECRET_KEY);
|
||||
|
||||
p.setCipherAlgorithm(cipherAlgorithm);
|
||||
|
||||
try {
|
||||
if (secretKey != null) {
|
||||
p.setSecretKey(secretKey);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.millisBetweenCleanUps = getInt(ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS);
|
||||
|
||||
this.privateKey = buildPrivateKey(getString(PRIVATE_KEY_PATH), getString(PRIVATE_KEY_ALGORITHM));
|
||||
super.initInternal(filterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
|
||||
|
||||
if (this.timer == null) {
|
||||
this.timer = new Timer(true);
|
||||
}
|
||||
|
||||
if (this.timerTask == null) {
|
||||
this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
|
||||
}
|
||||
this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps);
|
||||
}
|
||||
|
||||
private <T> T createNewTicketValidator(final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass, final String casServerUrlPrefix,
|
||||
final Class<T> clazz) {
|
||||
if (ticketValidatorClass == null) {
|
||||
return ReflectUtils.newInstance(clazz, casServerUrlPrefix);
|
||||
}
|
||||
|
||||
return (T) ReflectUtils.newInstance(ticketValidatorClass, casServerUrlPrefix);
|
||||
}
|
||||
|
||||
public static PrivateKey buildPrivateKey(final String keyPath, final String keyAlgorithm) {
|
||||
if (keyPath != null) {
|
||||
return PrivateKeyUtils.createKey(keyPath, keyAlgorithm);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,43 +160,64 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
* @param filterConfig the Filter Configuration object.
|
||||
* @return a fully constructed TicketValidator.
|
||||
*/
|
||||
@Override
|
||||
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
|
||||
final String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
|
||||
final String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null);
|
||||
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
|
||||
final boolean allowAnyProxy = getBoolean(ConfigurationKeys.ACCEPT_ANY_PROXY);
|
||||
final String allowedProxyChains = getString(ConfigurationKeys.ALLOWED_PROXY_CHAINS);
|
||||
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
|
||||
final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass = getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS);
|
||||
final Cas20ServiceTicketValidator validator;
|
||||
|
||||
if (CommonUtils.isNotBlank(allowAnyProxy) || CommonUtils.isNotBlank(allowedProxyChains)) {
|
||||
final Cas20ProxyTicketValidator v = new Cas20ProxyTicketValidator(casServerUrlPrefix);
|
||||
v.setAcceptAnyProxy(Boolean.parseBoolean(allowAnyProxy));
|
||||
v.setAllowedProxyChains(constructListOfProxies(allowedProxyChains));
|
||||
if (allowAnyProxy || CommonUtils.isNotBlank(allowedProxyChains)) {
|
||||
final Cas20ProxyTicketValidator v = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
|
||||
this.defaultProxyTicketValidatorClass);
|
||||
v.setAcceptAnyProxy(allowAnyProxy);
|
||||
v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
|
||||
validator = v;
|
||||
} else {
|
||||
validator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
|
||||
validator = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
|
||||
this.defaultServiceTicketValidatorClass);
|
||||
}
|
||||
validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
|
||||
validator.setProxyCallbackUrl(getString(ConfigurationKeys.PROXY_CALLBACK_URL));
|
||||
validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
|
||||
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix));
|
||||
validator.setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
|
||||
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(),
|
||||
getSSLConfig());
|
||||
validator.setURLConnectionFactory(factory);
|
||||
|
||||
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getString(ConfigurationKeys.ENCODING), factory));
|
||||
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
|
||||
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
|
||||
|
||||
final Map<String, String> additionalParameters = new HashMap<String, String>();
|
||||
final List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
|
||||
|
||||
for (final Enumeration<?> e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {
|
||||
final String s = (String) e.nextElement();
|
||||
|
||||
if (!params.contains(s)) {
|
||||
additionalParameters.put(s, filterConfig.getInitParameter(s));
|
||||
}
|
||||
}
|
||||
|
||||
validator.setPrivateKey(this.privateKey);
|
||||
|
||||
validator.setCustomParameters(additionalParameters);
|
||||
return validator;
|
||||
}
|
||||
|
||||
protected final List constructListOfProxies(final String proxies) {
|
||||
if (CommonUtils.isBlank(proxies)) {
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
final String[] splitProxies = proxies.split("\n");
|
||||
final List items = Arrays.asList(splitProxies);
|
||||
final ProxyListPropertyEditor editor = new ProxyListPropertyEditor();
|
||||
editor.setValue(items);
|
||||
return (List) editor.getValue();
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
this.timer.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* This processes the ProxyReceptor request before the ticket validation code executes.
|
||||
*/
|
||||
protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
|
||||
@Override
|
||||
protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
final String requestUri = request.getRequestURI();
|
||||
|
|
@ -114,29 +226,13 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
return true;
|
||||
}
|
||||
|
||||
final String proxyGrantingTicketIou = request
|
||||
.getParameter(PARAM_PROXY_GRANTING_TICKET_IOU);
|
||||
|
||||
final String proxyGrantingTicket = request
|
||||
.getParameter(PARAM_PROXY_GRANTING_TICKET);
|
||||
|
||||
if (CommonUtils.isBlank(proxyGrantingTicket)
|
||||
|| CommonUtils.isBlank(proxyGrantingTicketIou)) {
|
||||
response.getWriter().write("");
|
||||
return false;
|
||||
try {
|
||||
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
|
||||
} catch (final RuntimeException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received proxyGrantingTicketId ["
|
||||
+ proxyGrantingTicket + "] for proxyGrantingTicketIou ["
|
||||
+ proxyGrantingTicketIou + "]");
|
||||
}
|
||||
|
||||
this.proxyGrantingTicketStorage.save(proxyGrantingTicketIou,
|
||||
proxyGrantingTicket);
|
||||
|
||||
response.getWriter().write("<?xml version=\"1.0\"?>");
|
||||
response.getWriter().write("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\" />");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +240,19 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
this.proxyReceptorUrl = proxyReceptorUrl;
|
||||
}
|
||||
|
||||
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
|
||||
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
|
||||
public void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage storage) {
|
||||
this.proxyGrantingTicketStorage = storage;
|
||||
}
|
||||
|
||||
public void setTimer(final Timer timer) {
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
public void setTimerTask(final TimerTask timerTask) {
|
||||
this.timerTask = timerTask;
|
||||
}
|
||||
|
||||
public void setMillisBetweenCleanUps(final int millisBetweenCleanUps) {
|
||||
this.millisBetweenCleanUps = millisBetweenCleanUps;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,118 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
|
||||
/**
|
||||
* Extension to the traditional Service Ticket validation that will validate service tickets and proxy tickets.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator {
|
||||
public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator {
|
||||
|
||||
private boolean acceptAnyProxy;
|
||||
|
||||
/** This should be a list of an array of Strings */
|
||||
private List allowedProxyChains = new ArrayList();
|
||||
private ProxyList allowedProxyChains = new ProxyList();
|
||||
|
||||
/** Allows for an empty chain of proxy callback urls. **/
|
||||
private boolean allowEmptyProxyChain = true;
|
||||
|
||||
public Cas20ProxyTicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
protected final ProxyList getAllowedProxyChains() {
|
||||
return this.allowedProxyChains;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlSuffix() {
|
||||
return "proxyValidate";
|
||||
}
|
||||
|
||||
protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException {
|
||||
final List proxies = XmlUtils.getTextForElements(response, "proxy");
|
||||
final String[] proxiedList = (String[]) proxies.toArray(new String[proxies.size()]);
|
||||
@Override
|
||||
protected void customParseResponse(final String response, final Assertion assertion)
|
||||
throws TicketValidationException {
|
||||
final List<String> proxies = parseProxiesFromResponse(response);
|
||||
|
||||
if (proxies == null) {
|
||||
throw new InvalidProxyChainTicketValidationException(
|
||||
"Invalid proxy chain: No proxy could be retrieved from response. "
|
||||
+ "This indicates a problem with CAS validation. Review logs/configuration to find the root cause."
|
||||
);
|
||||
}
|
||||
// this means there was nothing in the proxy chain, which is okay
|
||||
if (proxies == null || proxies.isEmpty() || this.acceptAnyProxy) {
|
||||
if (this.allowEmptyProxyChain && proxies.isEmpty()) {
|
||||
logger.debug("Found an empty proxy chain, permitted by client configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
for (Iterator iter = this.allowedProxyChains.iterator(); iter.hasNext();) {
|
||||
if (Arrays.equals(proxiedList, (String[]) iter.next())) {
|
||||
return;
|
||||
}
|
||||
if (this.acceptAnyProxy) {
|
||||
logger.debug("Client configuration accepts any proxy. "
|
||||
+ "It is generally dangerous to use a non-proxied CAS filter "
|
||||
+ "specially for protecting resources that require proxy access.");
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] proxiedList = proxies.toArray(new String[proxies.size()]);
|
||||
if (this.allowedProxyChains.contains(proxiedList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn("Proxies received from the CAS validation response are {}. "
|
||||
+ "However, none are allowed by allowed proxy chain of the client which is {}",
|
||||
Arrays.toString(proxiedList), this.allowedProxyChains);
|
||||
|
||||
throw new InvalidProxyChainTicketValidationException("Invalid proxy chain: " + proxies.toString());
|
||||
}
|
||||
|
||||
public void setAcceptAnyProxy(final boolean acceptAnyProxy) {
|
||||
protected List<String> parseProxiesFromResponse(final String response) {
|
||||
return XmlUtils.getTextForElements(response, "proxy");
|
||||
}
|
||||
|
||||
public final void setAcceptAnyProxy(final boolean acceptAnyProxy) {
|
||||
this.acceptAnyProxy = acceptAnyProxy;
|
||||
}
|
||||
|
||||
public void setAllowedProxyChains(final List allowedProxyChains) {
|
||||
public final void setAllowedProxyChains(final ProxyList allowedProxyChains) {
|
||||
this.allowedProxyChains = allowedProxyChains;
|
||||
}
|
||||
|
||||
protected final boolean isAcceptAnyProxy() {
|
||||
return this.acceptAnyProxy;
|
||||
}
|
||||
|
||||
protected final boolean isAllowEmptyProxyChain() {
|
||||
return this.allowEmptyProxyChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to determine whether empty proxy chains are allowed.
|
||||
* @see #customParseResponse(String, Assertion)
|
||||
* @param allowEmptyProxyChain whether to allow empty proxy chains or not. True if so, false otherwise.
|
||||
*/
|
||||
public final void setAllowEmptyProxyChain(final boolean allowEmptyProxyChain) {
|
||||
this.allowEmptyProxyChain = allowEmptyProxyChain;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.SimpleBeanInfo;
|
||||
|
||||
import org.springframework.beans.propertyeditors.CustomBooleanEditor;
|
||||
|
||||
/**
|
||||
* BeanInfo support for using this class with Spring. Configures a ProxyListPropertyEditor to be used with the
|
||||
* Cas20ProxyTicketValidator when Spring is used to configure the CAS client.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class Cas20ProxyTicketValidatorBeanInfo extends SimpleBeanInfo {
|
||||
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
try {
|
||||
final PropertyDescriptor descriptor = new PropertyDescriptor("allowedProxyChains", Cas20ProxyTicketValidator.class, null, "setAllowedProxyChains") {
|
||||
public PropertyEditor createPropertyEditor(final Object bean) {
|
||||
return new ProxyListPropertyEditor();
|
||||
}
|
||||
};
|
||||
|
||||
final PropertyDescriptor acceptAnyProxy = new PropertyDescriptor("acceptAnyProxy", Cas20ProxyTicketValidator.class, null, "setAcceptAnyProxy") {
|
||||
public PropertyEditor createPropertyEditor(final Object bean) {
|
||||
return new CustomBooleanEditor(true);
|
||||
}
|
||||
};
|
||||
|
||||
return new PropertyDescriptor[] {descriptor, acceptAnyProxy};
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,31 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.*;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
|
||||
|
|
@ -12,18 +33,23 @@ import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
|||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* Implementation of the TicketValidator that will validate Service Tickets in compliance with the CAS 2.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTicketValidator {
|
||||
|
||||
public static final String PGT_ATTRIBUTE = "proxyGrantingTicket";
|
||||
private static final String PGTIOU_PREFIX = "PGTIOU-";
|
||||
|
||||
/** The CAS 2.0 protocol proxy callback url. */
|
||||
private String proxyCallbackUrl;
|
||||
|
||||
|
|
@ -33,6 +59,9 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
/** Implementation of the proxy retriever. */
|
||||
private ProxyRetriever proxyRetriever;
|
||||
|
||||
/** Private key for decryption */
|
||||
private PrivateKey privateKey;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the CAS 2.0 Service Ticket Validator with the supplied
|
||||
* CAS server url prefix.
|
||||
|
|
@ -41,7 +70,7 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
*/
|
||||
public Cas20ServiceTicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix);
|
||||
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding(), getURLConnectionFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -49,37 +78,40 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
*
|
||||
* @param urlParameters the Map containing the existing parameters to send to the server.
|
||||
*/
|
||||
protected final void populateUrlAttributeMap(final Map urlParameters) {
|
||||
urlParameters.put("pgtUrl", encodeUrl(this.proxyCallbackUrl));
|
||||
@Override
|
||||
protected final void populateUrlAttributeMap(final Map<String, String> urlParameters) {
|
||||
urlParameters.put("pgtUrl", this.proxyCallbackUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlSuffix() {
|
||||
return "serviceValidate";
|
||||
}
|
||||
|
||||
protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException {
|
||||
final String error = XmlUtils.getTextForElement(response,
|
||||
"authenticationFailure");
|
||||
@Override
|
||||
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
|
||||
final String error = parseAuthenticationFailureFromResponse(response);
|
||||
|
||||
if (CommonUtils.isNotBlank(error)) {
|
||||
throw new TicketValidationException(error);
|
||||
}
|
||||
|
||||
final String principal = XmlUtils.getTextForElement(response, "user");
|
||||
final String proxyGrantingTicketIou = XmlUtils.getTextForElement(
|
||||
response, "proxyGrantingTicket");
|
||||
final String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;
|
||||
final String principal = parsePrincipalFromResponse(response);
|
||||
final String proxyGrantingTicket = retrieveProxyGrantingTicket(response);
|
||||
|
||||
if (CommonUtils.isEmpty(principal)) {
|
||||
throw new TicketValidationException("No principal was found in the response from the CAS server.");
|
||||
}
|
||||
|
||||
final Assertion assertion;
|
||||
final Map<String, Object> attributes = extractCustomAttributes(response);
|
||||
if (CommonUtils.isNotBlank(proxyGrantingTicket)) {
|
||||
final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, proxyGrantingTicket, this.proxyRetriever);
|
||||
attributes.remove(PGT_ATTRIBUTE);
|
||||
final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes,
|
||||
proxyGrantingTicket, this.proxyRetriever);
|
||||
assertion = new AssertionImpl(attributePrincipal);
|
||||
} else {
|
||||
assertion = new AssertionImpl(principal);
|
||||
assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes));
|
||||
}
|
||||
|
||||
customParseResponse(response, assertion);
|
||||
|
|
@ -87,6 +119,87 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
return assertion;
|
||||
}
|
||||
|
||||
protected String retrieveProxyGrantingTicket(final String response) {
|
||||
final List<String> values = XmlUtils.getTextForElements(response, PGT_ATTRIBUTE);
|
||||
for (final String value : values) {
|
||||
if (value != null) {
|
||||
if (value.startsWith(PGTIOU_PREFIX)) {
|
||||
return retrieveProxyGrantingTicketFromStorage(value);
|
||||
} else {
|
||||
return retrieveProxyGrantingTicketViaEncryption(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String retrieveProxyGrantingTicketFromStorage(final String pgtIou) {
|
||||
if (this.proxyGrantingTicketStorage != null) {
|
||||
return this.proxyGrantingTicketStorage.retrieve(pgtIou);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String retrieveProxyGrantingTicketViaEncryption(final String encryptedPgt) {
|
||||
if (this.privateKey != null) {
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
|
||||
final byte[] cred64 = new Base64().decode(encryptedPgt);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
final byte[] cipherData = cipher.doFinal(cred64);
|
||||
final String pgt = new String(cipherData);
|
||||
logger.debug("Decrypted PGT: {}", pgt);
|
||||
return pgt;
|
||||
} catch (final Exception e) {
|
||||
logger.error("Unable to decrypt PGT", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String parsePrincipalFromResponse(final String response) {
|
||||
return XmlUtils.getTextForElement(response, "user");
|
||||
}
|
||||
|
||||
protected String parseAuthenticationFailureFromResponse(final String response) {
|
||||
return XmlUtils.getTextForElement(response, "authenticationFailure");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default attribute parsing of attributes that look like the following:
|
||||
* <cas:attributes>
|
||||
* <cas:attribute1>value</cas:attribute1>
|
||||
* <cas:attribute2>value</cas:attribute2>
|
||||
* </cas:attributes>
|
||||
* <p>
|
||||
*
|
||||
* Attributes look like following also parsed correctly:
|
||||
* <cas:attributes><cas:attribute1>value</cas:attribute1><cas:attribute2>value</cas:attribute2></cas:attributes>
|
||||
* <p>
|
||||
*
|
||||
* This code is here merely for sample/demonstration purposes for those wishing to modify the CAS2 protocol. You'll
|
||||
* probably want a more robust implementation or to use SAML 1.1
|
||||
*
|
||||
* @param xml the XML to parse.
|
||||
* @return the map of attributes.
|
||||
*/
|
||||
protected Map<String, Object> extractCustomAttributes(final String xml) {
|
||||
final SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
spf.setNamespaceAware(true);
|
||||
spf.setValidating(false);
|
||||
try {
|
||||
final SAXParser saxParser = spf.newSAXParser();
|
||||
final XMLReader xmlReader = saxParser.getXMLReader();
|
||||
final CustomAttributeHandler handler = new CustomAttributeHandler();
|
||||
xmlReader.setContentHandler(handler);
|
||||
xmlReader.parse(new InputSource(new StringReader(xml)));
|
||||
return handler.getAttributes();
|
||||
} catch (final Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method if additional custom parsing (such as Proxying) needs to be done.
|
||||
*
|
||||
|
|
@ -94,7 +207,8 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
* @param assertion the partially constructed assertion.
|
||||
* @throws TicketValidationException if there is a problem constructing the Assertion.
|
||||
*/
|
||||
protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException {
|
||||
protected void customParseResponse(final String response, final Assertion assertion)
|
||||
throws TicketValidationException {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
|
@ -108,5 +222,88 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
|
||||
public final void setProxyRetriever(final ProxyRetriever proxyRetriever) {
|
||||
this.proxyRetriever = proxyRetriever;
|
||||
}
|
||||
}
|
||||
|
||||
protected final String getProxyCallbackUrl() {
|
||||
return this.proxyCallbackUrl;
|
||||
}
|
||||
|
||||
protected final ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
|
||||
return this.proxyGrantingTicketStorage;
|
||||
}
|
||||
|
||||
protected final ProxyRetriever getProxyRetriever() {
|
||||
return this.proxyRetriever;
|
||||
}
|
||||
|
||||
private class CustomAttributeHandler extends DefaultHandler {
|
||||
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
private boolean foundAttributes;
|
||||
|
||||
private String currentAttribute;
|
||||
|
||||
private StringBuilder value;
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
this.attributes = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(final String namespaceURI, final String localName, final String qName,
|
||||
final Attributes attributes) throws SAXException {
|
||||
if ("attributes".equals(localName)) {
|
||||
this.foundAttributes = true;
|
||||
} else if (this.foundAttributes) {
|
||||
this.value = new StringBuilder();
|
||||
this.currentAttribute = localName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(final char[] chars, final int start, final int length) throws SAXException {
|
||||
if (this.currentAttribute != null) {
|
||||
value.append(chars, start, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(final String namespaceURI, final String localName, final String qName)
|
||||
throws SAXException {
|
||||
if ("attributes".equals(localName)) {
|
||||
this.foundAttributes = false;
|
||||
this.currentAttribute = null;
|
||||
} else if (this.foundAttributes) {
|
||||
final Object o = this.attributes.get(this.currentAttribute);
|
||||
|
||||
if (o == null) {
|
||||
this.attributes.put(this.currentAttribute, this.value.toString());
|
||||
} else {
|
||||
final List<Object> items;
|
||||
if (o instanceof List) {
|
||||
items = (List<Object>) o;
|
||||
} else {
|
||||
items = new LinkedList<Object>();
|
||||
items.add(o);
|
||||
this.attributes.put(this.currentAttribute, items);
|
||||
}
|
||||
items.add(this.value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public void setPrivateKey(final PrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.Protocol;
|
||||
|
||||
/**
|
||||
* Creates either a Cas30ProxyTicketValidator or a Cas30ServiceTicketValidator depending on whether any of the
|
||||
* proxy parameters are set.
|
||||
* <p/>
|
||||
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
|
||||
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class Cas30ProxyReceivingTicketValidationFilter extends Cas20ProxyReceivingTicketValidationFilter {
|
||||
|
||||
public Cas30ProxyReceivingTicketValidationFilter() {
|
||||
super(Protocol.CAS3);
|
||||
this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class;
|
||||
this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
/**
|
||||
* Service and proxy tickets validation service for the CAS protocol v3.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class Cas30ProxyTicketValidator extends Cas20ProxyTicketValidator {
|
||||
|
||||
public Cas30ProxyTicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlSuffix() {
|
||||
return "p3/proxyValidate";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Service tickets validation service for the CAS protocol v3.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public class Cas30ServiceTicketValidator extends Cas20ServiceTicketValidator {
|
||||
|
||||
public Cas30ServiceTicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlSuffix() {
|
||||
return "p3/serviceValidate";
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom attribute extractor that will account for inlined CAS attributes. Useful when CAS is acting as
|
||||
* as SAML 2 IdP and returns SAML attributes with names that contains namespaces.
|
||||
*
|
||||
* @param xml the XML to parse.
|
||||
* @return - Map of attributes
|
||||
*/
|
||||
@Override
|
||||
protected Map<String, Object> extractCustomAttributes(final String xml) {
|
||||
final Document document = XmlUtils.newDocument(xml);
|
||||
|
||||
// Check if attributes are inlined. If not return default super method results
|
||||
final NodeList attributeList = document.getElementsByTagName("cas:attribute");
|
||||
if (attributeList.getLength() == 0) {
|
||||
return super.extractCustomAttributes(xml);
|
||||
}
|
||||
|
||||
final HashMap<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
||||
for (int i = 0; i < attributeList.getLength(); i++) {
|
||||
final Node casAttributeNode = attributeList.item(i);
|
||||
final NamedNodeMap nodeAttributes = casAttributeNode.getAttributes();
|
||||
final String name = nodeAttributes.getNamedItem("name").getNodeValue();
|
||||
final String value = nodeAttributes.getNamedItem("value").getTextContent();
|
||||
final Object mapValue = attributes.get(name);
|
||||
if (mapValue != null) {
|
||||
if (mapValue instanceof List) {
|
||||
((List) mapValue).add(value);
|
||||
} else {
|
||||
final LinkedList<Object> list = new LinkedList<Object>();
|
||||
list.add(mapValue);
|
||||
list.add(value);
|
||||
attributes.put(name, list);
|
||||
}
|
||||
} else {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
|
|
@ -15,11 +28,11 @@ package org.jasig.cas.client.validation;
|
|||
public final class InvalidProxyChainTicketValidationException extends TicketValidationException {
|
||||
|
||||
/**
|
||||
* Unique Id for Serialization
|
||||
*/
|
||||
private static final long serialVersionUID = -7736653266370691534L;
|
||||
* Unique Id for Serialization
|
||||
*/
|
||||
private static final long serialVersionUID = -7736653266370691534L;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs an exception with the supplied message.
|
||||
* @param string the supplied message.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jasig.cas.client.authentication.ExactUrlPatternMatcherStrategy;
|
||||
import org.jasig.cas.client.authentication.RegexUrlPatternMatcherStrategy;
|
||||
import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Holding class for the proxy list to make Spring configuration easier.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1.3
|
||||
*/
|
||||
public final class ProxyList {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final List<List<UrlPatternMatcherStrategy>> proxyChains;
|
||||
|
||||
public ProxyList(final List<String[]> proxyChains) {
|
||||
CommonUtils.assertNotNull(proxyChains, "List of proxy chains cannot be null.");
|
||||
|
||||
this.proxyChains = new ArrayList<List<UrlPatternMatcherStrategy>>();
|
||||
|
||||
for (final String[] list : proxyChains) {
|
||||
final List<UrlPatternMatcherStrategy> chain = new ArrayList<UrlPatternMatcherStrategy>();
|
||||
|
||||
for (final String item : list) {
|
||||
if (item.startsWith("^")) {
|
||||
chain.add(new RegexUrlPatternMatcherStrategy(item));
|
||||
} else {
|
||||
chain.add(new ExactUrlPatternMatcherStrategy(item));
|
||||
}
|
||||
}
|
||||
|
||||
this.proxyChains.add(chain);
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyList() {
|
||||
this(new ArrayList<String[]>());
|
||||
}
|
||||
|
||||
public boolean contains(final String[] proxiedList) {
|
||||
StringBuilder loggingOutput;
|
||||
|
||||
for (final List<UrlPatternMatcherStrategy> proxyChain : this.proxyChains) {
|
||||
loggingOutput = new StringBuilder();
|
||||
|
||||
if (proxyChain.size() == proxiedList.length) {
|
||||
for (int linkIndex = 0; linkIndex < proxyChain.size(); linkIndex++) {
|
||||
final String linkToTest = proxiedList[linkIndex];
|
||||
loggingOutput.append(linkToTest);
|
||||
|
||||
if (proxyChain.get(linkIndex).matches(linkToTest)) {
|
||||
//If we are at the last link, we found a good proxyChain.
|
||||
if (linkIndex == proxyChain.size() - 1) {
|
||||
logger.info("Proxy chain matched: {}", loggingOutput.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warn("Proxy chain did not match at {}. Skipping to next allowedProxyChain", loggingOutput.toString());
|
||||
break;
|
||||
}
|
||||
loggingOutput.append("->");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn("No proxy chain matched the allowedProxyChains list.");
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.proxyChains.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Convert a String-formatted list of acceptable proxies to an array.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public final class ProxyListEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setAsText(final String text) throws IllegalArgumentException {
|
||||
final BufferedReader reader = new BufferedReader(new StringReader(text));
|
||||
final List<String[]> proxyChains = new ArrayList<String[]>();
|
||||
|
||||
try {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (CommonUtils.isNotBlank(line)) {
|
||||
proxyChains.add(line.trim().split(" "));
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
// ignore this
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (final IOException e) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
setValue(new ProxyList(proxyChains));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Convert a String-formatted list of acceptable proxies to an array.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public final class ProxyListPropertyEditor extends PropertyEditorSupport {
|
||||
|
||||
public void setAsText(final String text) throws IllegalArgumentException {
|
||||
final BufferedReader reader = new BufferedReader(new StringReader(text));
|
||||
final List proxyChains = new ArrayList();
|
||||
|
||||
try {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (CommonUtils.isNotBlank(line)) {
|
||||
proxyChains.add(line.trim().split(" "));
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
// ignore this
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (final IOException e) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
setValue(proxyChains);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
/**
|
||||
* Implementation of TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator.
|
||||
* <p>
|
||||
* Deployers can provide the "casServerUrlPrefix" and "tolerance" properties of the Saml11TicketValidator via the
|
||||
* context or filter init parameters.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class Saml11TicketValidationFilter extends AbstractTicketValidationFilter {
|
||||
|
||||
public Saml11TicketValidationFilter() {
|
||||
setArtifactParameterName("SAMLart");
|
||||
setServiceParameterName("TARGET");
|
||||
}
|
||||
|
||||
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
|
||||
final Saml11TicketValidator validator = new Saml11TicketValidator(getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null));
|
||||
final String tolerance = getPropertyFromInitParams(filterConfig, "tolerance", "1000");
|
||||
validator.setTolerance(Long.parseLong(tolerance));
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.opensaml.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator {
|
||||
|
||||
/** Time tolerance to allow for time drifting. */
|
||||
private long tolerance = 1000L;
|
||||
|
||||
public Saml11TicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
}
|
||||
|
||||
protected String getUrlSuffix() {
|
||||
return "samlValidate";
|
||||
}
|
||||
|
||||
protected void populateUrlAttributeMap(final Map urlParameters) {
|
||||
final String service = (String) urlParameters.get("service");
|
||||
urlParameters.remove("service");
|
||||
urlParameters.remove("ticket");
|
||||
urlParameters.put("TARGET", service);
|
||||
}
|
||||
|
||||
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
|
||||
try {
|
||||
final SAMLResponse samlResponse = new SAMLResponse(new ByteArrayInputStream(response.getBytes()));
|
||||
|
||||
if (!samlResponse.getAssertions().hasNext()) {
|
||||
throw new TicketValidationException("No assertions found.");
|
||||
}
|
||||
|
||||
for (final Iterator iter = samlResponse.getAssertions(); iter.hasNext();) {
|
||||
final SAMLAssertion assertion = (SAMLAssertion) iter.next();
|
||||
|
||||
if (!isValidAssertion(assertion)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final SAMLAuthenticationStatement authenticationStatement = getSAMLAuthenticationStatement(assertion);
|
||||
|
||||
if (authenticationStatement == null) {
|
||||
throw new TicketValidationException("No AuthentiationStatement found in SAML Assertion.");
|
||||
}
|
||||
final SAMLSubject subject = authenticationStatement.getSubject();
|
||||
|
||||
if (subject == null) {
|
||||
throw new TicketValidationException("No Subject found in SAML Assertion.");
|
||||
}
|
||||
|
||||
final SAMLAttribute[] attributes = getAttributesFor(assertion, subject);
|
||||
|
||||
final Map personAttributes = new HashMap();
|
||||
|
||||
for (int i = 0; i < attributes.length; i++) {
|
||||
final SAMLAttribute samlAttribute = attributes[i];
|
||||
final List values = getValuesFrom(samlAttribute);
|
||||
|
||||
personAttributes.put(samlAttribute.getName(), values.size() == 1 ? values.get(0) : values);
|
||||
}
|
||||
|
||||
final AttributePrincipal principal = new AttributePrincipalImpl(subject.getNameIdentifier().getName(), personAttributes);
|
||||
|
||||
|
||||
final Map authenticationAttributes = new HashMap();
|
||||
authenticationAttributes.put("samlAuthenticationStatement::authMethod", authenticationStatement.getAuthMethod());
|
||||
|
||||
final Assertion casAssertion = new AssertionImpl(principal, authenticationAttributes);
|
||||
return casAssertion;
|
||||
}
|
||||
} catch (final SAMLException e) {
|
||||
throw new TicketValidationException(e);
|
||||
}
|
||||
|
||||
throw new TicketValidationException("No valid assertions from the SAML response found.");
|
||||
}
|
||||
|
||||
private boolean isValidAssertion(final SAMLAssertion assertion) {
|
||||
final Date notBefore = assertion.getNotBefore();
|
||||
final Date notOnOrAfter = assertion.getNotOnOrAfter();
|
||||
|
||||
if (assertion.getNotBefore() == null || assertion.getNotOnOrAfter() == null) {
|
||||
log.debug("Assertion has no bounding dates. Will not process.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final long currentTime = new Date().getTime();
|
||||
|
||||
if (currentTime + tolerance < notBefore.getTime()) {
|
||||
log.debug("skipping assertion that's not yet valid...");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (notOnOrAfter.getTime() <= currentTime - tolerance) {
|
||||
log.debug("skipping expired assertion...");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SAMLAuthenticationStatement getSAMLAuthenticationStatement(final SAMLAssertion assertion) {
|
||||
for (final Iterator iter = assertion.getStatements(); iter.hasNext();) {
|
||||
final SAMLStatement statement = (SAMLStatement) iter.next();
|
||||
|
||||
if (statement instanceof SAMLAuthenticationStatement) {
|
||||
return (SAMLAuthenticationStatement) statement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SAMLAttribute[] getAttributesFor(final SAMLAssertion assertion, final SAMLSubject subject) {
|
||||
final List attributes = new ArrayList();
|
||||
for (final Iterator iter = assertion.getStatements(); iter.hasNext();) {
|
||||
final SAMLStatement statement = (SAMLStatement) iter.next();
|
||||
|
||||
if (statement instanceof SAMLAttributeStatement) {
|
||||
final SAMLAttributeStatement attributeStatement = (SAMLAttributeStatement) statement;
|
||||
// used because SAMLSubject does not implement equals
|
||||
if (subject.getNameIdentifier().getName().equals(attributeStatement.getSubject().getNameIdentifier().getName())) {
|
||||
for (final Iterator iter2 = attributeStatement.getAttributes(); iter2.hasNext();)
|
||||
attributes.add(iter2.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (SAMLAttribute[]) attributes.toArray(new SAMLAttribute[attributes.size()]);
|
||||
}
|
||||
|
||||
private List getValuesFrom(final SAMLAttribute attribute) {
|
||||
final List list = new ArrayList();
|
||||
for (final Iterator iter = attribute.getValues(); iter.hasNext();) {
|
||||
list.add(iter.next());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
protected String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
|
||||
final String MESSAGE_TO_SEND = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" MajorVersion=\"1\" MinorVersion=\"1\" RequestID=\"_192.168.16.51.1024506224022\" IssueInstant=\"2002-06-19T17:03:44.022Z\">"
|
||||
+ "<samlp:AssertionArtifact>" + ticket
|
||||
+ "</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection) validationUrl.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Length", Integer.toString(MESSAGE_TO_SEND.length()));
|
||||
conn.setRequestProperty("SOAPAction", "http://www.oasis-open.org/committees/security");
|
||||
conn.setUseCaches(false);
|
||||
conn.setDoInput(true);
|
||||
conn.setDoOutput(true);
|
||||
|
||||
final DataOutputStream out = new DataOutputStream(conn.getOutputStream());
|
||||
out.writeBytes(MESSAGE_TO_SEND);
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
final StringBuffer buffer = new StringBuffer(256);
|
||||
|
||||
synchronized (buffer) {
|
||||
String line;
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTolerance(final long tolerance) {
|
||||
this.tolerance = tolerance;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
|
|
@ -15,11 +28,11 @@ package org.jasig.cas.client.validation;
|
|||
public class TicketValidationException extends Exception {
|
||||
|
||||
/**
|
||||
* Unique Id for Serialization
|
||||
*/
|
||||
private static final long serialVersionUID = -7036248720402711806L;
|
||||
* Unique Id for Serialization
|
||||
*/
|
||||
private static final long serialVersionUID = -7036248720402711806L;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Constructs an exception with the supplied message.
|
||||
*
|
||||
* @param string the message
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
/*
|
||||
* Copyright 2007 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Licensed to Apereo under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Apereo licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.validation.json;
|
||||
|
||||
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
|
||||
|
||||
/**
|
||||
* Creates either a Cas30JsonServiceTicketValidator to validate tickets.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
*/
|
||||
public class Cas30JsonProxyReceivingTicketValidationFilter extends Cas30ProxyReceivingTicketValidationFilter {
|
||||
|
||||
public Cas30JsonProxyReceivingTicketValidationFilter() {
|
||||
super();
|
||||
this.defaultServiceTicketValidatorClass = Cas30JsonServiceTicketValidator.class;
|
||||
this.defaultProxyTicketValidatorClass = Cas30JsonProxyTicketValidator.class;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue