Compare commits
624 Commits
v0.22.3
...
test-bump-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7968d1b61d | ||
|
|
132f3a62d0 | ||
|
|
fbdcc0e30c | ||
|
|
be16cae9ae | ||
|
|
f80bf3af9b | ||
|
|
1df1bf34e0 | ||
|
|
31a5d640eb | ||
|
|
b45e11ce7f | ||
|
|
4388ecc4eb | ||
|
|
656d8075b2 | ||
|
|
8b209a202f | ||
|
|
2e66a13f48 | ||
|
|
d54ed26c71 | ||
|
|
5cf02ffae9 | ||
|
|
6afd0b16dc | ||
|
|
0b8af385f1 | ||
|
|
9a3063b7da | ||
|
|
71541029e7 | ||
|
|
076b803a5c | ||
|
|
97ef82f831 | ||
|
|
2ca484c691 | ||
|
|
4e5dcf0fc3 | ||
|
|
18e50975d4 | ||
|
|
d425af3c85 | ||
|
|
fce84d1de0 | ||
|
|
dc19984f23 | ||
|
|
8c1ecae68a | ||
|
|
862d692444 | ||
|
|
854a0c16b3 | ||
|
|
3d74e9762c | ||
|
|
3d8b98f563 | ||
|
|
06af416642 | ||
|
|
01920841d2 | ||
|
|
78aceb2b53 | ||
|
|
dce9bbd046 | ||
|
|
9f4573ade8 | ||
|
|
24af63da42 | ||
|
|
52c77fd388 | ||
|
|
19e6cc87c2 | ||
|
|
9bfed73b03 | ||
|
|
095241517b | ||
|
|
339885b2a0 | ||
|
|
6cfc985337 | ||
|
|
1c47685aea | ||
|
|
efa74a5f50 | ||
|
|
4c432cd356 | ||
|
|
0b07cb3885 | ||
|
|
8d13be5e87 | ||
|
|
3c34602f9e | ||
|
|
adfc96074f | ||
|
|
7cc7b874d1 | ||
|
|
4d12a5061d | ||
|
|
b274add4da | ||
|
|
fd51c9dc4c | ||
|
|
51f8794aa6 | ||
|
|
3db998f9c4 | ||
|
|
0a2a7087a7 | ||
|
|
8f0eb11ced | ||
|
|
e3e3599095 | ||
|
|
aa74e31453 | ||
|
|
e1f6e729fd | ||
|
|
13d83a6d1c | ||
|
|
6075387654 | ||
|
|
69fad3f55f | ||
|
|
e3864b62a4 | ||
|
|
22176f4e0f | ||
|
|
a89d7ec0ea | ||
|
|
8262049e20 | ||
|
|
c61e1e0a2a | ||
|
|
b376cf6c65 | ||
|
|
16bae25ce6 | ||
|
|
57ba17a12e | ||
|
|
f4d98a4910 | ||
|
|
fa32d78ff1 | ||
|
|
56f22a4479 | ||
|
|
7b88d3a1bc | ||
|
|
c8a39f9544 | ||
|
|
e77d1be53e | ||
|
|
6765bd0624 | ||
|
|
5f7b563a01 | ||
|
|
3885875149 | ||
|
|
cf05d5026f | ||
|
|
3e83a30739 | ||
|
|
49c5f5a8f0 | ||
|
|
72939e0cd7 | ||
|
|
271560894a | ||
|
|
6a591c1bcd | ||
|
|
3fdcfef1b4 | ||
|
|
7b8cfa2062 | ||
|
|
cc5921fd74 | ||
|
|
d027b7f759 | ||
|
|
f1524b0120 | ||
|
|
9985892751 | ||
|
|
cfd60bdd91 | ||
|
|
779f2a86e5 | ||
|
|
f47c4445bd | ||
|
|
1aeb4cc3d5 | ||
|
|
9e0a0205cc | ||
|
|
1058efb17a | ||
|
|
d0f744ebef | ||
|
|
a8c043cb16 | ||
|
|
978e02b5dc | ||
|
|
b39dbfff96 | ||
|
|
bd89cfde79 | ||
|
|
0bd563b2e5 | ||
|
|
22fe915629 | ||
|
|
aa161a5365 | ||
|
|
0557514cb4 | ||
|
|
298203253c | ||
|
|
cbeef2b248 | ||
|
|
e68a74ba48 | ||
|
|
02a0db1408 | ||
|
|
348376c672 | ||
|
|
037b02e268 | ||
|
|
fe534ab4e6 | ||
|
|
6625d54d67 | ||
|
|
ee6d1ed586 | ||
|
|
6de1d88e11 | ||
|
|
de19b6f17b | ||
|
|
226a90be1d | ||
|
|
6cfb6ff06a | ||
|
|
649c3d74b8 | ||
|
|
662ce3b2f5 | ||
|
|
0292bc154d | ||
|
|
ceee83f03a | ||
|
|
144904f0f6 | ||
|
|
963c8f1221 | ||
|
|
6c50c38f83 | ||
|
|
3aac62cc81 | ||
|
|
78a05d39c4 | ||
|
|
79bec3880e | ||
|
|
bdf7bd6309 | ||
|
|
4ce1ba999b | ||
|
|
d01501703b | ||
|
|
6a38a09462 | ||
|
|
78990e354f | ||
|
|
3189ebdfef | ||
|
|
39bf627b0a | ||
|
|
a838c763ea | ||
|
|
0afea63994 | ||
|
|
0df9487527 | ||
|
|
9274ee72ad | ||
|
|
2b6c3debb4 | ||
|
|
8dd6dd4e7f | ||
|
|
5e64c96497 | ||
|
|
54c0b4b8a2 | ||
|
|
31056e12ba | ||
|
|
151c8117a3 | ||
|
|
9b5c17c2db | ||
|
|
23e01b257e | ||
|
|
ecc8c7a86e | ||
|
|
ee4d7b9b69 | ||
|
|
80c03839a4 | ||
|
|
f394cb69ce | ||
|
|
52137ba9e5 | ||
|
|
942b8101cc | ||
|
|
a0a6b33ecd | ||
|
|
d4c5e1b51c | ||
|
|
96923aed75 | ||
|
|
a04f833e3f | ||
|
|
3b52cc9bd4 | ||
|
|
df2e92e451 | ||
|
|
8836fe043b | ||
|
|
7ce6a58099 | ||
|
|
57d6aca716 | ||
|
|
8d99637455 | ||
|
|
abd66780f4 | ||
|
|
a5175a35ec | ||
|
|
d9f945b5df | ||
|
|
0c55e39e8c | ||
|
|
b5443952da | ||
|
|
b9f0ccfaba | ||
|
|
24742325b7 | ||
|
|
c87ebe447f | ||
|
|
462cf16db9 | ||
|
|
edaa4e8754 | ||
|
|
76c596c574 | ||
|
|
b5554f6dcf | ||
|
|
fc65f1afd1 | ||
|
|
b066b6a920 | ||
|
|
bc0e63aac8 | ||
|
|
343ff575e6 | ||
|
|
08c922dca6 | ||
|
|
4dd6519cc6 | ||
|
|
cdffdae289 | ||
|
|
27e3b82223 | ||
|
|
239b31748a | ||
|
|
b465b74326 | ||
|
|
616f262d09 | ||
|
|
8aa0ec17c5 | ||
|
|
a8c5b53a2c | ||
|
|
e96dbd444e | ||
|
|
939e2acb0b | ||
|
|
a04955dc70 | ||
|
|
ba04a22492 | ||
|
|
a281fe129f | ||
|
|
a655cc8d3b | ||
|
|
63b584c83d | ||
|
|
7dffd5f079 | ||
|
|
044e5702df | ||
|
|
3c3b9546d9 | ||
|
|
5bc0e74b53 | ||
|
|
f0d4dddacd | ||
|
|
f4a3f46bcf | ||
|
|
38472e4cd2 | ||
|
|
9db5d1e4f4 | ||
|
|
4cadaf7d49 | ||
|
|
748486160f | ||
|
|
2c0a0b2bc4 | ||
|
|
7e51d4bebb | ||
|
|
c011e67122 | ||
|
|
394b4c403d | ||
|
|
74030aa067 | ||
|
|
07b8c745e6 | ||
|
|
6767bfa2d2 | ||
|
|
607d94fef4 | ||
|
|
83b060ef94 | ||
|
|
cb14cb94ce | ||
|
|
2b9de49fbe | ||
|
|
290f273bdf | ||
|
|
7b43779fb0 | ||
|
|
924c38faa6 | ||
|
|
e4d5f9610e | ||
|
|
04e9cb0ac8 | ||
|
|
da53daff37 | ||
|
|
8c26eff2c1 | ||
|
|
044c265423 | ||
|
|
0053658d5d | ||
|
|
99cf3b378f | ||
|
|
78293eab62 | ||
|
|
1a84be5782 | ||
|
|
178f82b675 | ||
|
|
ec5fbbcd1e | ||
|
|
78164054d4 | ||
|
|
6d5d11d5b4 | ||
|
|
9e3b93d385 | ||
|
|
d77cf93193 | ||
|
|
2cca3f3722 | ||
|
|
36d96a1791 | ||
|
|
82e34a5df2 | ||
|
|
7b83f4b1dc | ||
|
|
7d9910d1ca | ||
|
|
7a63f6da56 | ||
|
|
0043833f36 | ||
|
|
3ad3bccadb | ||
|
|
39e94c890e | ||
|
|
701039454a | ||
|
|
ec77a03d7c | ||
|
|
8dbad84a58 | ||
|
|
1767a37162 | ||
|
|
622c3a067a | ||
|
|
4389548b64 | ||
|
|
8cb0f1e558 | ||
|
|
faafb77c73 | ||
|
|
0ecd1c73c1 | ||
|
|
88bf40f9a6 | ||
|
|
a025163b34 | ||
|
|
07c80462b7 | ||
|
|
79ac2277d4 | ||
|
|
1c27bee9d0 | ||
|
|
56dc58b0b8 | ||
|
|
4018addd79 | ||
|
|
429dfb4314 | ||
|
|
1d7bb0bb2b | ||
|
|
476eb673bb | ||
|
|
71681b710c | ||
|
|
fb02a7da06 | ||
|
|
5fdc341138 | ||
|
|
15de6caf75 | ||
|
|
77bc2d5006 | ||
|
|
972f5fca9d | ||
|
|
083314ee2d | ||
|
|
078ce0e546 | ||
|
|
9beca2c226 | ||
|
|
451f23ae24 | ||
|
|
300ebfa19f | ||
|
|
1ce2846c95 | ||
|
|
7fb8c11a9d | ||
|
|
e7993c2d1b | ||
|
|
3e93f7ff88 | ||
|
|
f0580956db | ||
|
|
65b0bab26d | ||
|
|
a559421293 | ||
|
|
6ef35c26a4 | ||
|
|
8a918324aa | ||
|
|
d30c0c8cbb | ||
|
|
1697c826c0 | ||
|
|
b378b8c8ef | ||
|
|
2c42d7ff81 | ||
|
|
e1f36ee54a | ||
|
|
698f72f828 | ||
|
|
8dd94f5336 | ||
|
|
1dc21b9a21 | ||
|
|
248215cc77 | ||
|
|
a6eee73c11 | ||
|
|
50d6a39312 | ||
|
|
467b6b9bcb | ||
|
|
6e6246797c | ||
|
|
f8e4f747f5 | ||
|
|
9acd49fcc6 | ||
|
|
636d12d43f | ||
|
|
5e9383de95 | ||
|
|
4863af863e | ||
|
|
b6d4c62edd | ||
|
|
bf733f3822 | ||
|
|
bbf4027418 | ||
|
|
dbffc5fc22 | ||
|
|
f4a9420002 | ||
|
|
657854bd29 | ||
|
|
cdc9e7d921 | ||
|
|
b125121ac8 | ||
|
|
0c480dd5ec | ||
|
|
c07b8dcf73 | ||
|
|
912a4b216f | ||
|
|
e7fb205c31 | ||
|
|
a2ba20e12f | ||
|
|
f515dd82fe | ||
|
|
61d3193c41 | ||
|
|
430ae66955 | ||
|
|
3abbbc82b2 | ||
|
|
854181f63e | ||
|
|
df996794ed | ||
|
|
2d94018e3c | ||
|
|
72bb9d0ca1 | ||
|
|
93bd0d65e2 | ||
|
|
495b0f0068 | ||
|
|
3275b6a6d8 | ||
|
|
fed5aa1599 | ||
|
|
6bc4efbac1 | ||
|
|
cc0164a67b | ||
|
|
b968cc25ad | ||
|
|
d116a35a6d | ||
|
|
49f856bdd5 | ||
|
|
0d628f589a | ||
|
|
4387b2149f | ||
|
|
8cc602434e | ||
|
|
6411dc9504 | ||
|
|
32c34b0a11 | ||
|
|
6e8f5e0fc2 | ||
|
|
3ce377dbd1 | ||
|
|
ad502b9f18 | ||
|
|
fde186a5a3 | ||
|
|
0823f623c8 | ||
|
|
6cec113304 | ||
|
|
cd42d77a46 | ||
|
|
35907beaca | ||
|
|
a062a59288 | ||
|
|
842c2decd0 | ||
|
|
d1069ed359 | ||
|
|
6d81a1b1f8 | ||
|
|
b2fe478dae | ||
|
|
02ed6a6e8b | ||
|
|
1b271ab467 | ||
|
|
bacb5f8901 | ||
|
|
ae7371da95 | ||
|
|
60f5eb603b | ||
|
|
420ed00f55 | ||
|
|
b975871e9d | ||
|
|
803ffe2960 | ||
|
|
c96c95924c | ||
|
|
f02786001c | ||
|
|
542b7192c3 | ||
|
|
b31aa10b52 | ||
|
|
1eba59954d | ||
|
|
b71d9f05c5 | ||
|
|
fc5e94d55f | ||
|
|
3875fb3eaa | ||
|
|
b3d4132fe6 | ||
|
|
21f20bb9ea | ||
|
|
df937467a0 | ||
|
|
d1ae271111 | ||
|
|
e7fb3e0e45 | ||
|
|
b7b0271ec7 | ||
|
|
9935b839b7 | ||
|
|
0e11098c31 | ||
|
|
c55e0a069b | ||
|
|
c3da876b04 | ||
|
|
643a9c6c7c | ||
|
|
0c3a94172d | ||
|
|
527eb16700 | ||
|
|
e3aec3f094 | ||
|
|
eac2734df4 | ||
|
|
a411e7c977 | ||
|
|
28c4abe2d0 | ||
|
|
a9ef6ebf5f | ||
|
|
c12415c12d | ||
|
|
1f481e690b | ||
|
|
58aad859e1 | ||
|
|
b9ebfe09ee | ||
|
|
864cf7af99 | ||
|
|
b76f460979 | ||
|
|
cbd2c4682d | ||
|
|
d49bdf7d49 | ||
|
|
559a7278a0 | ||
|
|
4a172fae97 | ||
|
|
fc4263e2f9 | ||
|
|
c1b9b4c81b | ||
|
|
aa9b73522e | ||
|
|
0904f83627 | ||
|
|
253053cc23 | ||
|
|
08a3ff65c7 | ||
|
|
ee8fac8be8 | ||
|
|
9fa49b40b3 | ||
|
|
de13119e02 | ||
|
|
bf9acd7691 | ||
|
|
a5066fecc8 | ||
|
|
6432681440 | ||
|
|
b3b6df9d82 | ||
|
|
fe7be4ef62 | ||
|
|
b4603547f6 | ||
|
|
b33b9315ea | ||
|
|
6ae03fa028 | ||
|
|
e1bb1e0472 | ||
|
|
411670e4f5 | ||
|
|
1c55932f84 | ||
|
|
5a77054d6b | ||
|
|
84c5fd58f9 | ||
|
|
98979911ee | ||
|
|
c0cf7358c7 | ||
|
|
9053e64dff | ||
|
|
57bfe97d08 | ||
|
|
028570279c | ||
|
|
bda1cd1f25 | ||
|
|
7a9b775b09 | ||
|
|
17e791afb9 | ||
|
|
920fc7d937 | ||
|
|
6e314a2fa5 | ||
|
|
dc90db6591 | ||
|
|
beed4895c1 | ||
|
|
629dd669c4 | ||
|
|
fc9319e55b | ||
|
|
58b64a5739 | ||
|
|
d93537261e | ||
|
|
22ec87d00e | ||
|
|
b87b4156e7 | ||
|
|
93f010b880 | ||
|
|
c5d4cdf1bc | ||
|
|
c117601e53 | ||
|
|
f78f838ed9 | ||
|
|
1583b69fb7 | ||
|
|
cde6d1b0e0 | ||
|
|
be60569a14 | ||
|
|
51226a74d0 | ||
|
|
e983473a54 | ||
|
|
211ab3fd9d | ||
|
|
90c8ea7f09 | ||
|
|
fb5193d896 | ||
|
|
f7a7f01d7d | ||
|
|
2c84a52937 | ||
|
|
6020590b2f | ||
|
|
1477def4fe | ||
|
|
8882f1da0e | ||
|
|
056d487f1c | ||
|
|
b8083215b3 | ||
|
|
61c864e748 | ||
|
|
0e0f5030da | ||
|
|
0dacc4d49e | ||
|
|
4d783c5e42 | ||
|
|
75b3a6bea4 | ||
|
|
29507cda7e | ||
|
|
81e0c82fee | ||
|
|
7519fad6dd | ||
|
|
248a59ee8c | ||
|
|
cefb6d3c95 | ||
|
|
543076eaac | ||
|
|
cbf1ddeb4c | ||
|
|
3746adcc13 | ||
|
|
854b984850 | ||
|
|
62fa0e2043 | ||
|
|
4f5b1b0aa7 | ||
|
|
0e362c2106 | ||
|
|
6ac5be32b2 | ||
|
|
6966183bc8 | ||
|
|
60d70e3668 | ||
|
|
5e65f2aced | ||
|
|
1e345364cf | ||
|
|
502a6c462f | ||
|
|
ceafdb9cb4 | ||
|
|
a6d8f6beaa | ||
|
|
fa1f84bd0a | ||
|
|
72a1e5eefb | ||
|
|
f20fa0b1c8 | ||
|
|
07b7af59b6 | ||
|
|
c9ac525358 | ||
|
|
8b1b2b1e2d | ||
|
|
ad0591ec17 | ||
|
|
59c47d98dd | ||
|
|
cb38a545ce | ||
|
|
dd356b6ea9 | ||
|
|
c1d39a910f | ||
|
|
eb3913ba48 | ||
|
|
f7efbc66f7 | ||
|
|
f368d9936a | ||
|
|
5cb4e6f651 | ||
|
|
f7f7b087c4 | ||
|
|
4cceee8936 | ||
|
|
5262c02a28 | ||
|
|
fd81529ddc | ||
|
|
383341ad61 | ||
|
|
66d0182825 | ||
|
|
1fc9a40273 | ||
|
|
1953a98968 | ||
|
|
3b5979d783 | ||
|
|
9aa1f43df7 | ||
|
|
bf89f09238 | ||
|
|
b8e14ee269 | ||
|
|
c700ee491e | ||
|
|
65575751ff | ||
|
|
cc28f88325 | ||
|
|
dd913decc6 | ||
|
|
372852ee86 | ||
|
|
154974c24e | ||
|
|
844041fc32 | ||
|
|
c23aff45c8 | ||
|
|
1b4eacb587 | ||
|
|
f21434a971 | ||
|
|
6c0e7baa87 | ||
|
|
09fe3af6b7 | ||
|
|
50a6c61d0d | ||
|
|
53b5fba532 | ||
|
|
a73b710313 | ||
|
|
404b1ef92e | ||
|
|
c3b7af9c89 | ||
|
|
13d04b15de | ||
|
|
88fdd3a456 | ||
|
|
de4ff1c1f0 | ||
|
|
a6637f18c8 | ||
|
|
d4031ee7b5 | ||
|
|
7bf2b9601f | ||
|
|
6fbb4b568b | ||
|
|
7a14f0c012 | ||
|
|
e8f27228a2 | ||
|
|
8346fe4bd6 | ||
|
|
d1b8d7240e | ||
|
|
274b16a1a6 | ||
|
|
b0d8c332e5 | ||
|
|
ba1888b6c4 | ||
|
|
c0e47bafa4 | ||
|
|
894f6ce131 | ||
|
|
f0b39a6b6b | ||
|
|
24fdf3487b | ||
|
|
900b8d9f06 | ||
|
|
58b45b8ebc | ||
|
|
606a69b92c | ||
|
|
2c8b96c511 | ||
|
|
38e6af1d09 | ||
|
|
32a289d563 | ||
|
|
8d4d6eace0 | ||
|
|
f337a8dce7 | ||
|
|
fba06cf651 | ||
|
|
fecf484eda | ||
|
|
34907856c5 | ||
|
|
4a2915a62b | ||
|
|
8d51fe60f4 | ||
|
|
e96632a6b2 | ||
|
|
5ba7abaa79 | ||
|
|
451238d48b | ||
|
|
8df91922ad | ||
|
|
34d62837fd | ||
|
|
ed281bbe4a | ||
|
|
b218cbf503 | ||
|
|
c141b6d65e | ||
|
|
aacec617a8 | ||
|
|
ed19f3e1ad | ||
|
|
1374cf047e | ||
|
|
3a0e4c4f8c | ||
|
|
b34b05f059 | ||
|
|
579e8e05b7 | ||
|
|
a0463f405d | ||
|
|
ec344654db | ||
|
|
4aab5bc68f | ||
|
|
4c37afb446 | ||
|
|
86115afc6b | ||
|
|
87399d1064 | ||
|
|
a01b855d2f | ||
|
|
be39cb5af3 | ||
|
|
6930fd198c | ||
|
|
62e822df4e | ||
|
|
6466687a27 | ||
|
|
ccaaa21f16 | ||
|
|
0595ec166d | ||
|
|
72d1698242 | ||
|
|
9321e00d0a | ||
|
|
b03a49e4e1 | ||
|
|
aa9f6f02ca | ||
|
|
6a88d26054 | ||
|
|
37db97dba9 | ||
|
|
0ccce2b2bc | ||
|
|
58813eb4e0 | ||
|
|
5992edb3ba | ||
|
|
808e0ce3d5 | ||
|
|
534f3ec627 | ||
|
|
9d37c9f743 | ||
|
|
1bad4c3da6 | ||
|
|
9edb579156 | ||
|
|
d1aee5d431 | ||
|
|
dc8d4b4ca1 | ||
|
|
35ab508109 | ||
|
|
e01030820c | ||
|
|
fab041b364 | ||
|
|
e5da67d1bc | ||
|
|
bbc61930f9 | ||
|
|
40c789e7cb | ||
|
|
6084212bf6 | ||
|
|
944b56751d | ||
|
|
c9e53542e0 | ||
|
|
7001067baa | ||
|
|
aece2251cf | ||
|
|
756f0048f0 | ||
|
|
1cb2fca7a5 | ||
|
|
287af260b7 | ||
|
|
9fd76362dc | ||
|
|
733517fa76 | ||
|
|
d8b3d0715f | ||
|
|
1d951b28aa | ||
|
|
65ab6870e6 | ||
|
|
255e3d14d0 | ||
|
|
25e486ef18 | ||
|
|
92123bd243 | ||
|
|
6d5026d17e | ||
|
|
1e6ca131d9 | ||
|
|
21c6a441aa | ||
|
|
e23c110057 | ||
|
|
de16b73ce8 |
@@ -1,7 +0,0 @@
|
||||
node_modules/
|
||||
dist/
|
||||
target/
|
||||
console
|
||||
!console/
|
||||
portal-ui/node_modules/
|
||||
.git/
|
||||
49
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
49
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: community, triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## NOTE
|
||||
|
||||
Please subscribe to our [paid subscription plans](https://min.io/pricing) for 24x7 support from our Engineering team.
|
||||
|
||||
<!--- Provide a general summary of the issue in the title above -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behavior
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
|
||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
|
||||
|
||||
## Regression
|
||||
<!-- Is this issue a regression? (Yes / No) -->
|
||||
<!-- If Yes, optionally please include the MinIO version or commit id or PR# that caused this regression, if you have these details. -->
|
||||
|
||||
## Your Environment
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
* MinIO version used (`minio --version`):
|
||||
* Server setup and configuration:
|
||||
* Operating System and version (`uname -a`):
|
||||
18
.github/workflows/issues.yaml
vendored
Normal file
18
.github/workflows/issues.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# @format
|
||||
|
||||
name: Issue Workflow
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/miniohq/projects/2
|
||||
github-token: ${{ secrets.BOT_PAT }}
|
||||
1960
.github/workflows/jobs.yaml
vendored
1960
.github/workflows/jobs.yaml
vendored
File diff suppressed because it is too large
Load Diff
53
.github/workflows/vulncheck.yaml
vendored
Normal file
53
.github/workflows/vulncheck.yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# @format
|
||||
|
||||
name: Vulnerability Check
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
vulncheck:
|
||||
name: Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.3
|
||||
check-latest: true
|
||||
- name: Get official govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
shell: bash
|
||||
- name: Run govulncheck
|
||||
run: govulncheck ./...
|
||||
shell: bash
|
||||
|
||||
react-code-known-vulnerabilities:
|
||||
name: "React Code Has No Known Vulnerable Deps"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.23.x ]
|
||||
os: [ ubuntu-latest ]
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
- name: Read .nvmrc
|
||||
id: node_version
|
||||
run: echo "$(cat .nvmrc)" && echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_ENV
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NVMRC }}
|
||||
- name: Checks for known security issues with the installed packages
|
||||
working-directory: ./web-app
|
||||
continue-on-error: false
|
||||
run: |
|
||||
yarn npm audit --recursive --environment production --no-deprecations
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,3 +1,13 @@
|
||||
# Playwright Data
|
||||
web-app/storage/
|
||||
web-app/playwright/.auth/admin.json
|
||||
|
||||
# Report from Playwright
|
||||
web-app/playwright-report/
|
||||
|
||||
# Coverage from Playwright
|
||||
web-app/.nyc_output/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@@ -27,7 +37,7 @@ dist/
|
||||
|
||||
# Ignore node_modules
|
||||
|
||||
portal-ui/node_modules/
|
||||
web-app/node_modules/
|
||||
|
||||
# Ignore tls cert and key
|
||||
private.key
|
||||
|
||||
@@ -23,8 +23,8 @@ linters:
|
||||
- gomodguard
|
||||
- gofmt
|
||||
- unused
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- varcheck
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- durationcheck
|
||||
@@ -42,8 +42,5 @@ issues:
|
||||
- should have comment
|
||||
- use leading k in Go names
|
||||
- comment on exported const
|
||||
run:
|
||||
skip-dirs:
|
||||
- pkg/clientgen
|
||||
- pkg/apis/networking.gke.io
|
||||
- restapi/operations
|
||||
exclude-dirs:
|
||||
- api/operations
|
||||
|
||||
195
.goreleaser.yml
195
.goreleaser.yml
@@ -1,195 +0,0 @@
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
project_name: console
|
||||
|
||||
release:
|
||||
name_template: "Release version {{.Tag}}"
|
||||
github:
|
||||
owner: minio
|
||||
name: console
|
||||
extra_files:
|
||||
- glob: "*.minisig"
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# you may remove this if you don't use vgo
|
||||
- go mod tidy -compat=1.18
|
||||
|
||||
builds:
|
||||
-
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- ppc64le
|
||||
- s390x
|
||||
- arm64
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
main: ./cmd/console/
|
||||
|
||||
flags:
|
||||
- -trimpath
|
||||
- --tags=kqueue,operator
|
||||
|
||||
ldflags:
|
||||
- -s -w -X github.com/minio/console/pkg.ReleaseTag={{.Tag}} -X github.com/minio/console/pkg.CommitID={{.FullCommit}} -X github.com/minio/console/pkg.Version={{.Version}} -X github.com/minio/console/pkg.ShortCommitID={{.ShortCommit}} -X github.com/minio/console/pkg.ReleaseTime={{.Date}}
|
||||
|
||||
archives:
|
||||
-
|
||||
name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
|
||||
format: binary
|
||||
replacements:
|
||||
arm: arm
|
||||
|
||||
signs:
|
||||
-
|
||||
signature: "${artifact}.minisig"
|
||||
cmd: "sh"
|
||||
args:
|
||||
- '-c'
|
||||
- 'minisign -s /media/${USER}/minio/minisign.key -Sm ${artifact} < /media/${USER}/minio/minisign-passphrase'
|
||||
artifacts: all
|
||||
|
||||
snapshot:
|
||||
name_template: v0.0.0@{{.ShortCommit}}
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
|
||||
nfpms:
|
||||
-
|
||||
vendor: MinIO, Inc.
|
||||
homepage: https://github.com/minio/console
|
||||
maintainer: MinIO Development <dev@min.io>
|
||||
description: MinIO Console Server
|
||||
license: GNU Affero General Public License v3.0
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
contents:
|
||||
# Basic file that applies to all packagers
|
||||
- src: systemd/console.service
|
||||
dst: /etc/systemd/system/minio-console.service
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "minio/console:{{ .Tag }}-amd64"
|
||||
use: buildx
|
||||
goarch: amd64
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "minio/console:{{ .Tag }}-ppc64le"
|
||||
use: buildx
|
||||
goarch: ppc64le
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/ppc64le"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "minio/console:{{ .Tag }}-s390x"
|
||||
use: buildx
|
||||
goarch: s390x
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/s390x"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "minio/console:{{ .Tag }}-arm64"
|
||||
use: buildx
|
||||
goarch: arm64
|
||||
goos: linux
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "quay.io/minio/console:{{ .Tag }}-amd64"
|
||||
use: buildx
|
||||
goarch: amd64
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "quay.io/minio/console:{{ .Tag }}-ppc64le"
|
||||
use: buildx
|
||||
goarch: ppc64le
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/ppc64le"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "quay.io/minio/console:{{ .Tag }}-s390x"
|
||||
use: buildx
|
||||
goarch: s390x
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/s390x"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
- image_templates:
|
||||
- "quay.io/minio/console:{{ .Tag }}-arm64"
|
||||
use: buildx
|
||||
goarch: arm64
|
||||
goos: linux
|
||||
dockerfile: Dockerfile.release
|
||||
extra_files:
|
||||
- LICENSE
|
||||
- CREDITS
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64"
|
||||
- "--build-arg=TAG={{ .Tag }}"
|
||||
docker_manifests:
|
||||
- name_template: minio/console:{{ .Tag }}
|
||||
image_templates:
|
||||
- minio/console:{{ .Tag }}-amd64
|
||||
- minio/console:{{ .Tag }}-arm64
|
||||
- minio/console:{{ .Tag }}-ppc64le
|
||||
- minio/console:{{ .Tag }}-s390x
|
||||
- name_template: quay.io/minio/console:{{ .Tag }}
|
||||
image_templates:
|
||||
- quay.io/minio/console:{{ .Tag }}-amd64
|
||||
- quay.io/minio/console:{{ .Tag }}-arm64
|
||||
- quay.io/minio/console:{{ .Tag }}-ppc64le
|
||||
- quay.io/minio/console:{{ .Tag }}-s390x
|
||||
- name_template: minio/console:latest
|
||||
image_templates:
|
||||
- minio/console:{{ .Tag }}-amd64
|
||||
- minio/console:{{ .Tag }}-arm64
|
||||
- minio/console:{{ .Tag }}-ppc64le
|
||||
- minio/console:{{ .Tag }}-s390x
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
# Common large paths
|
||||
node_modules/
|
||||
portal-ui/node_modules/
|
||||
web-app/node_modules/
|
||||
build/
|
||||
dist/
|
||||
.idea/
|
||||
@@ -30,4 +30,6 @@ tests/
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
*~
|
||||
.eslintcache
|
||||
.eslintcache
|
||||
|
||||
consoleApi.ts
|
||||
439
CHANGELOG.md
Normal file
439
CHANGELOG.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# Changelog
|
||||
|
||||
## Release v1.7.5
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed leaks during ZIP multiobject downloads
|
||||
- Allow spaces in Policy names
|
||||
|
||||
## Release v1.7.4
|
||||
|
||||
Deprecations:
|
||||
|
||||
- Deprecated support tools User Interface in favor of mc admin commands. Please refer to the [MinIO SUBNET Registration page](https://min.io/docs/minio/linux/administration/console/subnet-registration.html#subnet) for more information.
|
||||
- Deprecated Site replication User Interface in favor of mc admin commands. Please refer to the [MinIO Site Replication page](https://min.io/docs/minio/linux/operations/install-deploy-manage/multi-site-replication.html) for more information.
|
||||
- Deprecated Lifecycle & Tiers User Interface in favor of mc admin commands. Please refer to the [MinIO Tiers page](https://min.io/docs/minio/linux/reference/minio-mc/mc-ilm-tier.html) for more information.
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Avoid loading unpkg.com call when login animation is off
|
||||
|
||||
## Release v1.7.3
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Use a fixed public license verification key
|
||||
- Show non-expiring access keys as `no-expiry` instead of Jan 1, 1970
|
||||
- Use "join Slack" button for non-commercial edition instead of "Signup"
|
||||
- Fix setting policies on groups that have spaces
|
||||
|
||||
## Release v1.7.2
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed issue in Server Health Info
|
||||
- Fixed Security vulnerability in dependencies
|
||||
- Fixed client string in trace message
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Remove live logs in Call Home Page
|
||||
- Update License page
|
||||
|
||||
## Release v1.7.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed issue that could cause a failure when attempting to view deleted files in the object browser
|
||||
- Return network error when logging in and the network connection fails
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Added debug logging for console HTTP request (see [PR #3440](https://github.com/minio/console/pull/3440) for more detailed information)
|
||||
|
||||
## Release v1.7.0
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed directory listing
|
||||
- Fix MinIO videos link
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Removed deprecated KES functionality
|
||||
|
||||
## Release v1.6.3
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Updated go.mod version
|
||||
|
||||
## Release v1.6.2
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed minor user session issues
|
||||
- Updated project dependencies
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Improved Drives List visualization
|
||||
- Improved WS request logic
|
||||
- Updated License page with current MinIO plans.
|
||||
|
||||
## Release v1.6.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed objectManager issues under certain conditions
|
||||
- Fixed Security vulnerability in dependencies
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Improved Share Link behavior
|
||||
|
||||
## Release v1.6.0
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed share link encoding
|
||||
- Fixed Edit Lifecycle Storage Class
|
||||
- Added Tiers Improvements for Bucket Lifecycle management
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Vulnerability updates
|
||||
- Update Logo logic
|
||||
|
||||
## Release v1.5.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added remove Tier functionality
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed ILM rule tags not being shown
|
||||
- Fixed race condition Object Browser websocket
|
||||
- Fixed Encryption page crashing on empty response
|
||||
- Fixed Replication Delete Marker comparisons
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Use automatic URI encoding for APIs
|
||||
- Vulnerability updates
|
||||
|
||||
## Release v1.4.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added VersionID support to metadata details
|
||||
- Improved Websockets handlers
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed vulnerabilities and updated dependencies
|
||||
- Fixed an issue with Download URL decoding
|
||||
- Fixed leak in Object Browser Websocket
|
||||
- Minor UX fixes
|
||||
|
||||
## Release v1.3.0
|
||||
|
||||
Features:
|
||||
|
||||
- Adds ExpireDeleteMarker status to BucketLifecycleRule UI
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed vulnerability
|
||||
- Used URL-safe base64 enconding for Share API
|
||||
- Made Prefix field optional when Adding Tier
|
||||
- Added Console user agent in MinIO Admin Client
|
||||
|
||||
## Release v1.2.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated file share logic to work as Proxy
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Updated project dependencies
|
||||
- Fixed Key Permissions UX
|
||||
- Added permissions validation to rewind button
|
||||
- Fixed Health report upload to SUBNET
|
||||
- Misc Cosmetic fixes
|
||||
|
||||
## Release v1.1.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed folder download issue
|
||||
|
||||
## Release v1.1.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added Set Expired object all versions selector
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Updated Go Dependencies
|
||||
|
||||
## Release v1.0.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated Preview message alert
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Updated Websocket API
|
||||
- Fixed issues with download manager
|
||||
- Fixed policies issues
|
||||
|
||||
## Release v0.46.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added latest help content to forms
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Disabled Create User button in certain policy cases
|
||||
- Fixed an issue with Logout request
|
||||
- Upgraded project dependencies
|
||||
|
||||
## Release v0.45.0
|
||||
|
||||
Deprecated:
|
||||
|
||||
- Deprecated Heal / Drives page
|
||||
|
||||
Features:
|
||||
|
||||
- Updated tines on menus & pages
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Upgraded project dependencies
|
||||
|
||||
## Release v0.44.0
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Upgraded project dependencies
|
||||
- Fixed events icons not loading in subpaths
|
||||
|
||||
## Release v0.43.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Update Share Object UI to reflect maximum expiration time in UI
|
||||
|
||||
## Release v0.43.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated PDF preview method
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed vulnerabilities
|
||||
- Prevented non-necessary metadata calls in object browser
|
||||
|
||||
## Release v0.42.2
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Hidden Prometheus metrics if URL is empty
|
||||
|
||||
## Release v0.42.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Reset go version to 1.19
|
||||
|
||||
## Release v0.42.0
|
||||
|
||||
Features:
|
||||
|
||||
- Introducing Dark Mode
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed vulnerabilities
|
||||
- Changes on Upload and Delete object urls
|
||||
- Fixed blocking subpath creation if not enough permissions
|
||||
- Removed share object option at prefix level
|
||||
- Updated allowed actions for a deleted object
|
||||
|
||||
## Release v0.41.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated pages to use mds components
|
||||
- support for resolving IPv4/IPv6
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Remove cache for ClientIP
|
||||
- Fixed override environment variables display in settings page
|
||||
- Fixed daylight savings time support in share modal
|
||||
|
||||
## Release v0.40.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated OpenID page
|
||||
- Added New bucket event types support
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed crash in access keys page
|
||||
- Fixed AuditLog filters issue
|
||||
- Fixed multiple issues with Object Browser
|
||||
|
||||
## Release v0.39.0
|
||||
|
||||
Features:
|
||||
|
||||
- Migrated metrics page to mds
|
||||
- Migrated Register page to mds
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed LDAP configuration page issues
|
||||
- Load available certificates in logout
|
||||
- Updated dependencies & go version
|
||||
- Fixed delete objects functionality
|
||||
|
||||
## Release v0.38.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added extra information to Service Accounts page
|
||||
- Updated Tiers, Site Replication, Speedtest, Heal & Watch pages components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed IDP expiry time errors
|
||||
- Updated project Dependencies
|
||||
|
||||
## Release v0.37.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated Trace and Logs page components
|
||||
- Updated Prometheus metrics
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Disabled input fields for Subscription features if MinIO is not registered
|
||||
|
||||
## Release v0.36.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated Settings page components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Show LDAP Enabled value LDAP configuration
|
||||
- Download multiple objects in same path as they were selected
|
||||
|
||||
## Release v0.35.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Change timestamp format for zip creation
|
||||
|
||||
## Release v0.35.0
|
||||
|
||||
Features:
|
||||
|
||||
- Add Exclude Folders and Exclude Prefixes during bucket creation
|
||||
- Download multiple selected objects as zip and ignore deleted objects
|
||||
- Updated Call Home, Inspet, Profile and Health components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Remove extra white spaces for configuration strings
|
||||
- Allow Create New Path in bucket view when having right permissions
|
||||
|
||||
## Release v0.34.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated Buckets components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed SUBNET Health report upload
|
||||
- Updated Download Handler
|
||||
- Fixes issue with rewind
|
||||
- Avoid 1 hour expiration for IDP credentials
|
||||
|
||||
---
|
||||
|
||||
## Release v0.33.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated OpenID, LDAP components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed security issues
|
||||
- Fixed navigation issues in Object Browser
|
||||
- Fixed Dashboard metrics
|
||||
|
||||
---
|
||||
|
||||
## Release v0.32.0
|
||||
|
||||
Features:
|
||||
|
||||
- Updated Users and Groups components
|
||||
- Added placeholder image for Help Menu
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed memory leak in WebSocket API for Object Browser
|
||||
|
||||
---
|
||||
|
||||
## Release v0.31.0
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
- **Removed support for Standalone Deployments**
|
||||
|
||||
Features:
|
||||
|
||||
- Updated way files are displayed in uploading component
|
||||
- Updated Audit Logs and Policies components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed Download folders issue in Object Browser
|
||||
- Added missing Notification Events (ILM & REPLICA) in Events Notification Page
|
||||
- Fixed Security Vulnerability for `semver` dependency
|
||||
|
||||
---
|
||||
|
||||
## Release v0.30.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added MinIO Console Help Menu
|
||||
- Updated UI Menu components
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Disable the Upload button on Object Browser if the user is not allowed
|
||||
- Fixed security vulnerability for `lestrrat-go/jwx` and `fast-xml-parser`
|
||||
- Fixed bug on sub-paths for Object Browser
|
||||
- Reduce the number of calls to `/session` API endpoint to improve performance
|
||||
- Rolled back the previous change for the Share File feature to no longer ask for Service Account access keys
|
||||
@@ -4,56 +4,80 @@ This is a REST portal server created using [go-swagger](https://github.com/go-sw
|
||||
|
||||
The API handlers are created using a YAML definition located in `swagger.YAML`.
|
||||
|
||||
To add new api, the YAML file needs to be updated with all the desired apis using the [Swagger Basic Structure](https://swagger.io/docs/specification/2-0/basic-structure/), this includes paths, parameters, definitions, tags, etc.
|
||||
To add new api, the YAML file needs to be updated with all the desired apis using
|
||||
the [Swagger Basic Structure](https://swagger.io/docs/specification/2-0/basic-structure/), this includes paths,
|
||||
parameters, definitions, tags, etc.
|
||||
|
||||
## Generate server from YAML
|
||||
|
||||
Once the YAML file is ready we can autogenerate the code needed for the new api by just running:
|
||||
|
||||
Validate it:
|
||||
|
||||
```
|
||||
swagger validate ./swagger.yml
|
||||
```
|
||||
|
||||
Update server code:
|
||||
|
||||
```
|
||||
make swagger-gen
|
||||
```
|
||||
|
||||
This will update all the necessary code.
|
||||
|
||||
`./restapi/configure_console.go` is a file that contains the handlers to be used by the application, here is the only place where we need to update our code to support the new apis. This file is not affected when running the swagger generator and it is safe to edit.
|
||||
`./api/configure_console.go` is a file that contains the handlers to be used by the application, here is the only place
|
||||
where we need to update our code to support the new apis. This file is not affected when running the swagger generator
|
||||
and it is safe to edit.
|
||||
|
||||
## Unit Tests
|
||||
`./restapi/handlers_test.go` needs to be updated with the proper tests for the new api.
|
||||
|
||||
`./api/handlers_test.go` needs to be updated with the proper tests for the new api.
|
||||
|
||||
To run tests:
|
||||
|
||||
```
|
||||
go test ./restapi
|
||||
go test ./api
|
||||
```
|
||||
|
||||
## Commit changes
|
||||
After verification, commit your changes. This is a [great post](https://chris.beams.io/posts/git-commit/) on how to write useful commit messages
|
||||
|
||||
After verification, commit your changes. This is a [great post](https://chris.beams.io/posts/git-commit/) on how to
|
||||
write useful commit messages
|
||||
|
||||
```
|
||||
$ git commit -am 'Add some feature'
|
||||
```
|
||||
|
||||
### Push to the branch
|
||||
|
||||
Push your locally committed changes to the remote origin (your fork)
|
||||
|
||||
```
|
||||
$ git push origin my-new-feature
|
||||
```
|
||||
|
||||
### Create a Pull Request
|
||||
Pull requests can be created via GitHub. Refer to [this document](https://help.github.com/articles/creating-a-pull-request/) for detailed steps on how to create a pull request. After a Pull Request gets peer reviewed and approved, it will be merged.
|
||||
|
||||
Pull requests can be created via GitHub. Refer
|
||||
to [this document](https://help.github.com/articles/creating-a-pull-request/) for detailed steps on how to create a pull
|
||||
request. After a Pull Request gets peer reviewed and approved, it will be merged.
|
||||
|
||||
## FAQs
|
||||
|
||||
### How does ``console`` manages dependencies?
|
||||
|
||||
``MinIO`` uses `go mod` to manage its dependencies.
|
||||
|
||||
- Run `go get foo/bar` in the source folder to add the dependency to `go.mod` file.
|
||||
|
||||
To remove a dependency
|
||||
|
||||
- Edit your code and remove the import reference.
|
||||
- Run `go mod tidy` in the source folder to remove dependency from `go.mod` file.
|
||||
|
||||
### What are the coding guidelines for console?
|
||||
``console`` is fully conformant with Golang style. Refer: [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project. If you observe offending code, please feel free to send a pull request or ping us on [Slack](https://slack.min.io).
|
||||
|
||||
``console`` is fully conformant with Golang style.
|
||||
Refer: [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project. If you observe
|
||||
offending code, please feel free to send a pull request or ping us on [Slack](https://slack.min.io).
|
||||
|
||||
@@ -1,3 +1,82 @@
|
||||
# Developing MinIO Console
|
||||
|
||||
The MinIO Console requires the [MinIO Server](https://github.com/minio/minio). For development purposes, you also need
|
||||
to run both the MinIO Console web app and the MinIO Console server.
|
||||
|
||||
## Running MinIO Console server
|
||||
|
||||
Build the server in the main folder by running:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
> Note: If it's the first time running the server, you might need to run `go mod tidy` to ensure you have all modules
|
||||
> required.
|
||||
> To start the server run:
|
||||
|
||||
```
|
||||
CONSOLE_ACCESS_KEY=<your-access-key>
|
||||
CONSOLE_SECRET_KEY=<your-secret-key>
|
||||
CONSOLE_MINIO_SERVER=<minio-server-endpoint>
|
||||
CONSOLE_DEV_MODE=on
|
||||
./console server
|
||||
```
|
||||
|
||||
## Running MinIO Console web app
|
||||
|
||||
Refer to `/web-app` [instructions](/web-app/README.md) to run the web app locally.
|
||||
|
||||
# Building with MinIO
|
||||
|
||||
To test console in its shipping format, you need to build it from the MinIO repository, the following step will guide
|
||||
you to do that.
|
||||
|
||||
### 0. Building with UI Changes
|
||||
|
||||
If you are performing changes in the UI components of console and want to test inside the MinIO binary, you need to
|
||||
build assets first.
|
||||
|
||||
In the console folder run
|
||||
|
||||
```shell
|
||||
make assets
|
||||
```
|
||||
|
||||
This will regenerate all the static assets that will be served by MinIO.
|
||||
|
||||
### 1. Clone the `MinIO` repository
|
||||
|
||||
In the parent folder of where you cloned this `console` repository, clone the MinIO Repository
|
||||
|
||||
```shell
|
||||
git clone https://github.com/minio/minio.git
|
||||
```
|
||||
|
||||
### 2. Update `go.mod` to use your local version
|
||||
|
||||
In the MinIO repository open `go.mod` and after the first `require()` directive add a `replace()` directive
|
||||
|
||||
```
|
||||
...
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/minio/console => "../console"
|
||||
)
|
||||
|
||||
require (
|
||||
...
|
||||
```
|
||||
|
||||
### 3. Build `MinIO`
|
||||
|
||||
Still in the MinIO folder, run
|
||||
|
||||
```shell
|
||||
make build
|
||||
```
|
||||
|
||||
# LDAP authentication with Console
|
||||
|
||||
## Setup
|
||||
@@ -15,7 +94,8 @@ $ docker cp console/docs/ldap/billy.ldif my-openldap-container:/container/servic
|
||||
$ docker exec my-openldap-container ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin -f /container/service/slapd/assets/test/billy.ldif -H ldap://localhost
|
||||
```
|
||||
|
||||
Query the ldap server to check the user billy was created correctly and got assigned to the consoleAdmin group, you should get a list
|
||||
Query the ldap server to check the user billy was created correctly and got assigned to the consoleAdmin group, you
|
||||
should get a list
|
||||
containing ldap users and groups.
|
||||
|
||||
```
|
||||
@@ -30,7 +110,7 @@ $ docker exec my-openldap-container ldapsearch -x -H ldap://localhost -b uid=bil
|
||||
|
||||
### Change the password for user billy
|
||||
|
||||
Set the new password for `billy` to `minio123` and enter `admin` as the default `LDAP Password`
|
||||
Set the new password for `billy` to `minio123` and enter `admin` as the default `LDAP Password`
|
||||
|
||||
```
|
||||
$ docker exec -it my-openldap-container /bin/bash
|
||||
@@ -41,6 +121,7 @@ Enter LDAP Password:
|
||||
```
|
||||
|
||||
### Add the consoleAdmin policy to user billy on MinIO
|
||||
|
||||
```
|
||||
$ cat > consoleAdmin.json << EOF
|
||||
{
|
||||
@@ -66,8 +147,8 @@ $ cat > consoleAdmin.json << EOF
|
||||
]
|
||||
}
|
||||
EOF
|
||||
$ mc admin policy add myminio consoleAdmin consoleAdmin.json
|
||||
$ mc admin policy set myminio consoleAdmin user="uid=billy,dc=example,dc=org"
|
||||
$ mc admin policy create myminio consoleAdmin consoleAdmin.json
|
||||
$ mc admin policy attach myminio consoleAdmin --user="uid=billy,dc=example,dc=org"
|
||||
```
|
||||
|
||||
## Run MinIO
|
||||
|
||||
43
Dockerfile
43
Dockerfile
@@ -1,43 +0,0 @@
|
||||
ARG NODE_VERSION
|
||||
FROM node:$NODE_VERSION as uilayer
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./portal-ui/package.json ./
|
||||
COPY ./portal-ui/yarn.lock ./
|
||||
RUN yarn install
|
||||
|
||||
COPY ./portal-ui .
|
||||
|
||||
RUN make build-static
|
||||
|
||||
USER node
|
||||
|
||||
FROM golang:1.18 as golayer
|
||||
|
||||
RUN apt-get update -y && apt-get install -y ca-certificates
|
||||
|
||||
ADD go.mod /go/src/github.com/minio/console/go.mod
|
||||
ADD go.sum /go/src/github.com/minio/console/go.sum
|
||||
WORKDIR /go/src/github.com/minio/console/
|
||||
|
||||
# Get dependencies - will also be cached if we won't change mod/sum
|
||||
RUN go mod download
|
||||
|
||||
ADD . /go/src/github.com/minio/console/
|
||||
WORKDIR /go/src/github.com/minio/console/
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
COPY --from=uilayer /app/build /go/src/github.com/minio/console/portal-ui/build
|
||||
RUN go build --tags=kqueue,operator -ldflags "-w -s" -a -o console ./cmd/console
|
||||
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6
|
||||
MAINTAINER MinIO Development "dev@min.io"
|
||||
EXPOSE 9090
|
||||
|
||||
|
||||
COPY --from=golayer /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=golayer /go/src/github.com/minio/console/console .
|
||||
|
||||
ENTRYPOINT ["/console"]
|
||||
@@ -1,14 +0,0 @@
|
||||
ARG NODE_VERSION
|
||||
FROM node:$NODE_VERSION as uilayer
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./portal-ui/package.json ./
|
||||
COPY ./portal-ui/yarn.lock ./
|
||||
RUN yarn install
|
||||
|
||||
COPY ./portal-ui .
|
||||
|
||||
RUN yarn install && make build-static
|
||||
|
||||
USER node
|
||||
@@ -1,23 +0,0 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.6
|
||||
|
||||
ARG TAG
|
||||
|
||||
COPY CREDITS /licenses/CREDITS
|
||||
COPY LICENSE /licenses/LICENSE
|
||||
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="${TAG}" \
|
||||
release="${TAG}" \
|
||||
summary="A graphical user interface for MinIO" \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
RUN \
|
||||
microdnf update --nodocs && \
|
||||
microdnf install ca-certificates --nodocs
|
||||
|
||||
EXPOSE 9090
|
||||
COPY console /console
|
||||
|
||||
ENTRYPOINT ["/console"]
|
||||
120
Makefile
120
Makefile
@@ -13,12 +13,7 @@ default: console
|
||||
.PHONY: console
|
||||
console:
|
||||
@echo "Building Console binary to './console'"
|
||||
@(GO111MODULE=on CGO_ENABLED=0 go build -trimpath --tags=kqueue,operator --ldflags "-s -w" -o console ./cmd/console)
|
||||
|
||||
k8sdev:
|
||||
@docker build -t $(TAG) --build-arg build_version=$(BUILD_VERSION) --build-arg build_time='$(BUILD_TIME)' --build-arg NODE_VERSION='$(NODE_VERSION)' .
|
||||
@kind load docker-image $(TAG)
|
||||
@echo "Done, now restart your console deployment"
|
||||
@(GO111MODULE=on CGO_ENABLED=0 go build -trimpath --tags=kqueue --ldflags "-s -w" -o console ./cmd/console)
|
||||
|
||||
getdeps:
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
@@ -38,12 +33,16 @@ lint:
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint cache clean
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint run --timeout=5m --config ./.golangci.yml
|
||||
|
||||
lint-fix: getdeps ## runs golangci-lint suite of linters with automatic fixes
|
||||
@echo "Running $@ check"
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint run --timeout=5m --config ./.golangci.yml --fix
|
||||
|
||||
install: console
|
||||
@echo "Installing console binary to '$(GOPATH)/bin/console'"
|
||||
@mkdir -p $(GOPATH)/bin && cp -f $(PWD)/console $(GOPATH)/bin/console
|
||||
@echo "Installation successful. To learn more, try \"console --help\"."
|
||||
|
||||
swagger-gen: clean-swagger swagger-console swagger-operator apply-gofmt
|
||||
swagger-gen: clean-swagger swagger-console apply-gofmt
|
||||
@echo "Done Generating swagger server code from yaml"
|
||||
|
||||
apply-gofmt:
|
||||
@@ -53,35 +52,40 @@ apply-gofmt:
|
||||
clean-swagger:
|
||||
@echo "cleaning"
|
||||
@rm -rf models
|
||||
@rm -rf restapi/operations
|
||||
@rm -rf operatorapi/operations
|
||||
@rm -rf api/operations
|
||||
|
||||
swagger-console:
|
||||
@echo "Generating swagger server code from yaml"
|
||||
@swagger generate server -A console --main-package=management --server-package=restapi --exclude-main -P models.Principal -f ./swagger-console.yml -r NOTICE
|
||||
@swagger generate server -A console --main-package=management --server-package=api --exclude-main -P models.Principal -f ./swagger.yml -r NOTICE
|
||||
@echo "Generating typescript api"
|
||||
@npx swagger-typescript-api -p ./swagger.yml -o ./web-app/src/api -n consoleApi.ts --custom-config generator.config.js
|
||||
@git restore api/server.go
|
||||
|
||||
swagger-operator:
|
||||
@echo "Generating swagger server code from yaml"
|
||||
@swagger generate server -A operator --main-package=operator --server-package=operatorapi --exclude-main -P models.Principal -f ./swagger-operator.yml -r NOTICE
|
||||
|
||||
assets:
|
||||
@(if [ -f "${NVM_DIR}/nvm.sh" ]; then \. "${NVM_DIR}/nvm.sh" && nvm install && nvm use && npm install -g yarn ; fi &&\
|
||||
cd portal-ui; yarn install --prefer-offline; make build-static; yarn prettier --write . --loglevel warn; cd ..)
|
||||
cd web-app; corepack enable; yarn install --prefer-offline; make build-static; yarn prettier --write . --log-level warn; cd ..)
|
||||
|
||||
test-integration:
|
||||
@(docker stop pgsqlcontainer || true)
|
||||
@(docker stop minio || true)
|
||||
@(docker stop minio2 || true)
|
||||
@(docker network rm mynet123 || true)
|
||||
@echo "create docker network to communicate containers MinIO & PostgreSQL"
|
||||
@(docker network create --subnet=173.18.0.0/29 mynet123)
|
||||
@echo "docker run with MinIO Version below:"
|
||||
@echo $(MINIO_VERSION)
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 --net=mynet123 -d --name minio --rm -p 9000:9000 -p 9001:9001 -e MINIO_KMS_SECRET_KEY=my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw= $(MINIO_VERSION) server /data{1...4} --console-address ':9001' && sleep 5)
|
||||
@(docker run --net=mynet123 --ip=173.18.0.3 --name pgsqlcontainer --rm -p 5432:5432 -e POSTGRES_PASSWORD=password -d postgres && sleep 5)
|
||||
@echo "MinIO 1"
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 --net=mynet123 -d --name minio --rm -p 9000:9000 -p 9091:9091 -e MINIO_KMS_SECRET_KEY=my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw= $(MINIO_VERSION) server /data{1...4} --console-address ':9091' && sleep 5)
|
||||
@echo "MinIO 2"
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 --net=mynet123 -d --name minio2 --rm -p 9001:9001 -p 9092:9092 -e MINIO_KMS_SECRET_KEY=my-minio-key:OSMM+vkKUTCvQs9YL/CVMIMt43HFhkUpqJxTmGl6rYw= $(MINIO_VERSION) server /data{1...4} --address ':9001' --console-address ':9092' && sleep 5)
|
||||
@echo "Postgres"
|
||||
@(docker run --net=mynet123 --ip=173.18.0.4 --name pgsqlcontainer --rm -p 5432:5432 -e POSTGRES_PASSWORD=password -d postgres && sleep 5)
|
||||
@echo "execute test and get coverage for test-integration:"
|
||||
@(cd integration && go test -coverpkg=../restapi -c -tags testrunmain . && mkdir -p coverage && export THETARGET=$(TARGET_BUCKET) && echo "THETARGET: ${THETARGET}" && ./integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/system.out)
|
||||
@(cd integration && go test -coverpkg=../api -c -tags testrunmain . && mkdir -p coverage && ./integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/system.out)
|
||||
@(docker stop pgsqlcontainer)
|
||||
@(docker stop minio)
|
||||
@(docker stop minio2)
|
||||
@(docker network rm mynet123)
|
||||
|
||||
test-replication:
|
||||
@@ -126,7 +130,7 @@ test-replication:
|
||||
$(MINIO_VERSION) server /data{1...4} \
|
||||
--address :9002 \
|
||||
--console-address :6002)
|
||||
@(cd replication && go test -coverpkg=../restapi -c -tags testrunmain . && mkdir -p coverage && ./replication.test -test.v -test.run "^Test*" -test.coverprofile=coverage/replication.out)
|
||||
@(cd replication && go test -coverpkg=../api -c -tags testrunmain . && mkdir -p coverage && ./replication.test -test.v -test.run "^Test*" -test.coverprofile=coverage/replication.out)
|
||||
@(docker stop minio || true)
|
||||
@(docker stop minio1 || true)
|
||||
@(docker stop minio2 || true)
|
||||
@@ -174,88 +178,98 @@ test-sso-integration:
|
||||
@(docker run --name minio-client --network my-net -dit --entrypoint=/bin/sh minio/mc)
|
||||
@(docker exec minio-client mc alias set myminio/ http://minio:9000 minio minio123)
|
||||
@echo "adding policy to Dillon Harper to be able to login:"
|
||||
@(cd sso-integration && docker cp allaccess.json minio-client:/ && docker exec minio-client mc admin policy add myminio "Dillon Harper" allaccess.json)
|
||||
@(cd sso-integration && docker cp allaccess.json minio-client:/ && docker exec minio-client mc admin policy create myminio "Dillon Harper" allaccess.json)
|
||||
@echo "starting bash script"
|
||||
@(env bash $(PWD)/sso-integration/set-sso.sh)
|
||||
@echo "add python module"
|
||||
@(pip3 install bs4)
|
||||
@echo "Executing the test:"
|
||||
@(cd sso-integration && go test -coverpkg=../restapi -c -tags testrunmain . && mkdir -p coverage && ./sso-integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/sso-system.out)
|
||||
|
||||
test-operator-integration:
|
||||
@(echo "Start cd operator-integration && go test:")
|
||||
@(pwd)
|
||||
@(cd operator-integration && go test -coverpkg=../operatorapi -c -tags testrunmain . && mkdir -p coverage && ./operator-integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/operator-api.out)
|
||||
|
||||
test-operator:
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/operator.sh)
|
||||
@(docker stop minio)
|
||||
@(cd sso-integration && go test -coverpkg=../api -c -tags testrunmain . && mkdir -p coverage && ./sso-integration.test -test.v -test.run "^Test*" -test.coverprofile=coverage/sso-system.out)
|
||||
|
||||
test-permissions-1:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-1/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-1/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-2:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-2/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-2/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-3:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-3/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-3/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-4:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-4/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-4/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-5:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-5/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-5/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-6:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-6/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-permissions-7:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/permissions.sh "portal-ui/tests/permissions-7/")
|
||||
@(env bash $(PWD)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-6/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-apply-permissions:
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/initialize-env.sh)
|
||||
@(env bash $(PWD)/web-app/tests/scripts/initialize-env.sh)
|
||||
|
||||
test-start-docker-minio:
|
||||
@(docker run -v /data1 -v /data2 -v /data3 -v /data4 -d --name minio --rm -p 9000:9000 quay.io/minio/minio:latest server /data{1...4})
|
||||
|
||||
initialize-operator:
|
||||
@echo "Done initializing operator test"
|
||||
|
||||
initialize-permissions: test-start-docker-minio test-apply-permissions
|
||||
@echo "Done initializing permissions test"
|
||||
|
||||
cleanup-permissions:
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/cleanup-env.sh)
|
||||
@(env bash $(PWD)/web-app/tests/scripts/cleanup-env.sh)
|
||||
@(docker stop minio)
|
||||
|
||||
initialize-docker-network:
|
||||
@(docker network create test-network)
|
||||
|
||||
test-start-docker-minio-w-redirect-url: initialize-docker-network
|
||||
@(docker run \
|
||||
-e MINIO_BROWSER_REDIRECT_URL='http://localhost:8000/console/subpath/' \
|
||||
-e MINIO_SERVER_URL='http://localhost:9000' \
|
||||
-v /data1 -v /data2 -v /data3 -v /data4 \
|
||||
-d --network host --name minio --rm\
|
||||
quay.io/minio/minio:latest server /data{1...4})
|
||||
|
||||
test-start-docker-nginx-w-subpath:
|
||||
@(docker run \
|
||||
--network host \
|
||||
-d --rm \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
-v ./web-app/tests/subpath-nginx/nginx.conf:/etc/nginx/nginx.conf \
|
||||
--name test-nginx nginx)
|
||||
|
||||
test-initialize-minio-nginx: test-start-docker-minio-w-redirect-url test-start-docker-nginx-w-subpath
|
||||
|
||||
cleanup-minio-nginx:
|
||||
@(docker stop minio test-nginx & docker network rm test-network)
|
||||
|
||||
# https://stackoverflow.com/questions/19200235/golang-tests-in-sub-directory
|
||||
# Note: go test ./... will run tests on the current folder and all subfolders.
|
||||
# This is needed because tests can be in the folder or sub-folder(s), let's include them all please!.
|
||||
test:
|
||||
@echo "execute test and get coverage"
|
||||
@(cd restapi && mkdir coverage && GO111MODULE=on go test -test.v -coverprofile=coverage/coverage.out)
|
||||
@(cd api && mkdir -p coverage && GO111MODULE=on go test ./... -test.v -coverprofile=coverage/coverage.out)
|
||||
|
||||
test-unit-test-operator:
|
||||
@echo "execute unit test and get coverage for operatorapi"
|
||||
@(cd operatorapi && mkdir coverage && GO111MODULE=on go test -test.v -coverprofile=coverage/coverage-unit-test-operatorapi.out)
|
||||
|
||||
# https://stackoverflow.com/questions/19200235/golang-tests-in-sub-directory
|
||||
# Note: go test ./... will run tests on the current folder and all subfolders.
|
||||
# This is since tests in pkg folder are in subfolders and were not executed.
|
||||
test-pkg:
|
||||
@echo "execute test and get coverage"
|
||||
@(cd pkg && mkdir coverage && GO111MODULE=on go test -test.v -coverprofile=coverage/coverage-pkg.out)
|
||||
@(cd pkg && mkdir -p coverage && GO111MODULE=on go test ./... -test.v -coverprofile=coverage/coverage-pkg.out)
|
||||
|
||||
coverage:
|
||||
@(GO111MODULE=on go test -v -coverprofile=coverage.out github.com/minio/console/restapi/... && go tool cover -html=coverage.out && open coverage.html)
|
||||
@(GO111MODULE=on go test -v -coverprofile=coverage.out github.com/minio/console/api/... && go tool cover -html=coverage.out && open coverage.html)
|
||||
|
||||
clean:
|
||||
@echo "Cleaning up all the generated files"
|
||||
@@ -269,7 +283,5 @@ docker:
|
||||
release: swagger-gen
|
||||
@echo "Generating Release: $(RELEASE)"
|
||||
@make assets
|
||||
@yq -i e '.spec.template.spec.containers[0].image |= "minio/console:$(RELEASE)"' k8s/operator-console/base/console-deployment.yaml
|
||||
@yq -i e 'select(.kind == "Deployment").spec.template.spec.containers[0].image |= "minio/console:$(RELEASE)"' k8s/operator-console/standalone/console-deployment.yaml
|
||||
@git add -u .
|
||||
@git add portal-ui/build/
|
||||
@git add web-app/build/
|
||||
|
||||
2
NOTICE
2
NOTICE
@@ -1,5 +1,5 @@
|
||||
This file is part of MinIO Console Server
|
||||
Copyright (c) 2022 MinIO, Inc.
|
||||
Copyright (c) 2023 MinIO, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
|
||||
163
README.md
163
README.md
@@ -4,61 +4,28 @@
|
||||
|
||||
A graphical user interface for [MinIO](https://github.com/minio/minio)
|
||||
|
||||
| Dashboard | Creating a bucket |
|
||||
| ------------- | ------------- |
|
||||
|  |  |
|
||||
| Object Browser | Dashboard | Creating a bucket |
|
||||
|------------------------------------|-------------------------------|-------------------------------|
|
||||
|  |  |  |
|
||||
|
||||
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
||||
**Table of Contents**
|
||||
|
||||
- [MinIO Console](#minio-console)
|
||||
- [Install](#install)
|
||||
- [Binary Releases](#binary-releases)
|
||||
- [Docker](#docker)
|
||||
- [Build from source](#build-from-source)
|
||||
- [Setup](#setup)
|
||||
- [1. Create a user `console` using `mc`](#1-create-a-user-console-using-mc)
|
||||
- [2. Create a policy for `console` with admin access to all resources (for testing)](#2-create-a-policy-for-console-with-admin-access-to-all-resources-for-testing)
|
||||
- [3. Set the policy for the new `console` user](#3-set-the-policy-for-the-new-console-user)
|
||||
- [Start Console service:](#start-console-service)
|
||||
- [Start Console service with TLS:](#start-console-service-with-tls)
|
||||
- [Connect Console to a Minio using TLS and a self-signed certificate](#connect-console-to-a-minio-using-tls-and-a-self-signed-certificate)
|
||||
- [Install](#install)
|
||||
- [Build from source](#build-from-source)
|
||||
- [Setup](#setup)
|
||||
- [1. Create a user `console` using `mc`](#1-create-a-user-console-using-mc)
|
||||
- [2. Create a policy for `console` with admin access to all resources (for testing)](#2-create-a-policy-for-console-with-admin-access-to-all-resources-for-testing)
|
||||
- [3. Set the policy for the new `console` user](#3-set-the-policy-for-the-new-console-user)
|
||||
- [Start Console service:](#start-console-service)
|
||||
- [Start Console service with TLS:](#start-console-service-with-tls)
|
||||
- [Connect Console to a Minio using TLS and a self-signed certificate](#connect-console-to-a-minio-using-tls-and-a-self-signed-certificate)
|
||||
- [Contribute to console Project](#contribute-to-console-project)
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
|
||||
## Install
|
||||
|
||||
### Binary Releases
|
||||
|
||||
| OS | ARCH | Binary |
|
||||
|:-------:|:-------:|:----------------------------------------------------------------------------------------------------:|
|
||||
| Linux | amd64 | [linux-amd64](https://github.com/minio/console/releases/latest/download/console-linux-amd64) |
|
||||
| Linux | arm64 | [linux-arm64](https://github.com/minio/console/releases/latest/download/console-linux-arm64) |
|
||||
| Linux | ppc64le | [linux-ppc64le](https://github.com/minio/console/releases/latest/download/console-linux-ppc64le) |
|
||||
| Linux | s390x | [linux-s390x](https://github.com/minio/console/releases/latest/download/console-linux-s390x) |
|
||||
| Apple | amd64 | [darwin-amd64](https://github.com/minio/console/releases/latest/download/console-darwin-amd64) |
|
||||
| Windows | amd64 | [windows-amd64](https://github.com/minio/console/releases/latest/download/console-windows-amd64.exe) |
|
||||
|
||||
You can also verify the binary with [minisign](https://jedisct1.github.io/minisign/) by downloading the corresponding [`.minisig`](https://github.com/minio/console/releases/latest) signature file. Then run:
|
||||
```
|
||||
minisign -Vm console-<OS>-<ARCH> -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Pull the latest release via:
|
||||
```
|
||||
docker pull minio/console
|
||||
```
|
||||
|
||||
### Build from source
|
||||
> You will need a working Go environment. Therefore, please follow [How to install Go](https://golang.org/doc/install).
|
||||
> Minimum version required is go1.18
|
||||
|
||||
```
|
||||
go install github.com/minio/console/cmd/console@latest
|
||||
```
|
||||
MinIO Console is a library that provides a management and browser UI overlay for the MinIO Server.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -103,60 +70,64 @@ EOF
|
||||
```
|
||||
|
||||
```sh
|
||||
mc admin policy add myminio/ consoleAdmin admin.json
|
||||
mc admin policy create myminio/ consoleAdmin admin.json
|
||||
```
|
||||
|
||||
### 3. Set the policy for the new `console` user
|
||||
|
||||
```sh
|
||||
mc admin policy set myminio consoleAdmin user=console
|
||||
mc admin policy attach myminio consoleAdmin --user=console
|
||||
```
|
||||
|
||||
> NOTE: Additionally, you can create policies to limit the privileges for other `console` users, for example, if you want the user to only have access to dashboard, buckets, notifications and watch page, the policy should look like this:
|
||||
> NOTE: Additionally, you can create policies to limit the privileges for other `console` users, for example, if you
|
||||
> want the user to only have access to dashboard, buckets, notifications and watch page, the policy should look like
|
||||
> this:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Action": [
|
||||
"admin:ServerInfo"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"s3:ListenBucketNotification",
|
||||
"s3:PutBucketNotification",
|
||||
"s3:GetBucketNotification",
|
||||
"s3:ListMultipartUploadParts",
|
||||
"s3:ListBucketMultipartUploads",
|
||||
"s3:ListBucket",
|
||||
"s3:HeadBucket",
|
||||
"s3:GetObject",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:AbortMultipartUpload",
|
||||
"s3:CreateBucket",
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteBucket",
|
||||
"s3:PutBucketPolicy",
|
||||
"s3:DeleteBucketPolicy",
|
||||
"s3:GetBucketPolicy"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
],
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"admin:ServerInfo"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
},
|
||||
{
|
||||
"Action": [
|
||||
"s3:ListenBucketNotification",
|
||||
"s3:PutBucketNotification",
|
||||
"s3:GetBucketNotification",
|
||||
"s3:ListMultipartUploadParts",
|
||||
"s3:ListBucketMultipartUploads",
|
||||
"s3:ListBucket",
|
||||
"s3:HeadBucket",
|
||||
"s3:GetObject",
|
||||
"s3:GetBucketLocation",
|
||||
"s3:AbortMultipartUpload",
|
||||
"s3:CreateBucket",
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject",
|
||||
"s3:DeleteBucket",
|
||||
"s3:PutBucketPolicy",
|
||||
"s3:DeleteBucketPolicy",
|
||||
"s3:GetBucketPolicy"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
],
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Start Console service:
|
||||
|
||||
Before running console service, following environment settings must be supplied
|
||||
|
||||
```sh
|
||||
# Salt to encrypt JWT payload
|
||||
export CONSOLE_PBKDF_PASSPHRASE=SECRET
|
||||
@@ -169,6 +140,7 @@ export CONSOLE_MINIO_SERVER=http://localhost:9000
|
||||
```
|
||||
|
||||
Now start the console service.
|
||||
|
||||
```
|
||||
./console server
|
||||
2021-01-19 02:36:08.893735 I | 2021/01/19 02:36:08 server.go:129: Serving console at http://localhost:9090
|
||||
@@ -189,6 +161,7 @@ Copy your `public.crt` and `private.key` to `~/.console/certs`, then:
|
||||
For advanced users, `console` has support for multiple certificates to service clients through multiple domains.
|
||||
|
||||
Following tree structure is expected for supporting multiple domains:
|
||||
|
||||
```sh
|
||||
certs/
|
||||
│
|
||||
@@ -218,5 +191,27 @@ export CONSOLE_MINIO_SERVER=https://localhost:9000
|
||||
|
||||
You can verify that the apis work by doing the request on `localhost:9090/api/v1/...`
|
||||
|
||||
## Debug logging
|
||||
|
||||
In some cases it may be convenient to log all HTTP requests. This can be enabled by setting
|
||||
the `CONSOLE_DEBUG_LOGLEVEL` environment variable to one of the following values:
|
||||
|
||||
- `0` (default) uses no logging.
|
||||
- `1` log single line per request for server-side errors (status-code 5xx).
|
||||
- `2` log single line per request for client-side and server-side errors (status-code 4xx/5xx).
|
||||
- `3` log single line per request for all requests (status-code 4xx/5xx).
|
||||
- `4` log details per request for server-side errors (status-code 5xx).
|
||||
- `5` log details per request for client-side and server-side errors (status-code 4xx/5xx).
|
||||
- `6` log details per request for all requests (status-code 4xx/5xx).
|
||||
|
||||
A single line logging has the following information:
|
||||
- Remote endpoint (IP + port) of the request. Note that reverse proxies may hide the actual remote endpoint of the client's browser.
|
||||
- HTTP method and URL
|
||||
- Status code of the response (websocket connections are hijacked, so no response is shown)
|
||||
- Duration of the request
|
||||
|
||||
The detailed logging also includes all request and response headers (if any).
|
||||
|
||||
# Contribute to console Project
|
||||
|
||||
Please follow console [Contributor's Guide](https://github.com/minio/console/blob/master/CONTRIBUTING.md)
|
||||
|
||||
@@ -18,9 +18,10 @@ you need access credentials for a successful exploit).
|
||||
|
||||
If you have not received a reply to your email within 48 hours or you have not heard from the security team
|
||||
for the past five days please contact the security team directly:
|
||||
- Primary security coordinator: lenin@min.io
|
||||
- Secondary coordinator: security@min.io
|
||||
- If you receive no response: dev@min.io
|
||||
|
||||
- Primary security coordinator: daniel@min.io
|
||||
- Secondary coordinator: security@min.io
|
||||
- If you receive no response: dev@min.io
|
||||
|
||||
### Disclosure Process
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
systemApi "github.com/minio/console/restapi/operations/system"
|
||||
systemApi "github.com/minio/console/api/operations/system"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
)
|
||||
|
||||
func registerAdminArnsHandlers(api *operations.ConsoleAPI) {
|
||||
@@ -31,7 +31,7 @@ func registerAdminArnsHandlers(api *operations.ConsoleAPI) {
|
||||
api.SystemArnListHandler = systemApi.ArnListHandlerFunc(func(params systemApi.ArnListParams, session *models.Principal) middleware.Responder {
|
||||
arnsResp, err := getArnsResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewArnListDefault(int(err.Code)).WithPayload(err)
|
||||
return systemApi.NewArnListDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return systemApi.NewArnListOK().WithPayload(arnsResp)
|
||||
})
|
||||
@@ -50,10 +50,10 @@ func getArns(ctx context.Context, client MinioAdmin) (*models.ArnsResponse, erro
|
||||
}
|
||||
|
||||
// getArnsResponse returns a list of active arns in the instance
|
||||
func getArnsResponse(session *models.Principal, params systemApi.ArnListParams) (*models.ArnsResponse, *models.Error) {
|
||||
func getArnsResponse(session *models.Principal, params systemApi.ArnListParams) (*models.ArnsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -25,21 +25,21 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations/system"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations/system"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestArnsList(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
// Test-1 : getArns() returns proper arn list
|
||||
minioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{
|
||||
SQSARN: []string{"uno"},
|
||||
}, nil
|
||||
@@ -54,7 +54,7 @@ func TestArnsList(t *testing.T) {
|
||||
assert.Nil(err, "Error should have been nil")
|
||||
|
||||
// Test-2 : getArns(ctx) fails for whatever reason
|
||||
minioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, errors.New("some reason")
|
||||
}
|
||||
|
||||
349
api/admin_client_mock.go
Normal file
349
api/admin_client_mock.go
Normal file
@@ -0,0 +1,349 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
type AdminClientMock struct{}
|
||||
|
||||
var (
|
||||
MinioServerInfoMock func(ctx context.Context) (madmin.InfoMessage, error)
|
||||
minioChangePasswordMock func(ctx context.Context, accessKey, secretKey string) error
|
||||
|
||||
minioHelpConfigKVMock func(subSys, key string, envOnly bool) (madmin.Help, error)
|
||||
minioGetConfigKVMock func(key string) ([]byte, error)
|
||||
minioSetConfigKVMock func(kv string) (restart bool, err error)
|
||||
minioDelConfigKVMock func(name string) (err error)
|
||||
minioHelpConfigKVGlobalMock func(envOnly bool) (madmin.Help, error)
|
||||
|
||||
minioGetLogsMock func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
|
||||
minioListGroupsMock func() ([]string, error)
|
||||
minioUpdateGroupMembersMock func(madmin.GroupAddRemove) error
|
||||
minioGetGroupDescriptionMock func(group string) (*madmin.GroupDesc, error)
|
||||
minioSetGroupStatusMock func(group string, status madmin.GroupStatus) error
|
||||
|
||||
minioHealMock func(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
|
||||
|
||||
minioServerHealthInfoMock func(ctx context.Context, deadline time.Duration) (interface{}, string, error)
|
||||
|
||||
minioListPoliciesMock func() (map[string]*iampolicy.Policy, error)
|
||||
minioGetPolicyMock func(name string) (*iampolicy.Policy, error)
|
||||
minioRemovePolicyMock func(name string) error
|
||||
minioAddPolicyMock func(name string, policy *iampolicy.Policy) error
|
||||
minioSetPolicyMock func(policyName, entityName string, isGroup bool) error
|
||||
|
||||
minioStartProfiling func(profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error)
|
||||
|
||||
minioServiceRestartMock func(ctx context.Context) error
|
||||
|
||||
getSiteReplicationInfo func(ctx context.Context) (*madmin.SiteReplicationInfo, error)
|
||||
addSiteReplicationInfo func(ctx context.Context, sites []madmin.PeerSite) (*madmin.ReplicateAddStatus, error)
|
||||
editSiteReplicationInfo func(ctx context.Context, site madmin.PeerInfo) (*madmin.ReplicateEditStatus, error)
|
||||
deleteSiteReplicationInfoMock func(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
|
||||
getSiteReplicationStatus func(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
|
||||
|
||||
minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error)
|
||||
minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error)
|
||||
minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error
|
||||
minioRemoveTierMock func(ctx context.Context, tierName string) error
|
||||
minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error
|
||||
minioVerifyTierStatusMock func(ctx context.Context, tierName string) error
|
||||
|
||||
minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
|
||||
minioListUsersMock func() (map[string]madmin.UserInfo, error)
|
||||
minioAddUserMock func(accessKey, secreyKey string) error
|
||||
minioRemoveUserMock func(accessKey string) error
|
||||
minioGetUserInfoMock func(accessKey string) (madmin.UserInfo, error)
|
||||
minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
|
||||
|
||||
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
|
||||
minioAddServiceAccountMock func(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error)
|
||||
minioListServiceAccountsMock func(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
|
||||
minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
|
||||
minioInfoServiceAccountMock func(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
|
||||
minioUpdateServiceAccountMock func(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
|
||||
minioGetLDAPPolicyEntitiesMock func(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
|
||||
|
||||
minioListRemoteBucketsMock func(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
|
||||
minioGetRemoteBucketMock func(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
|
||||
minioAddRemoteBucketMock func(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
|
||||
)
|
||||
|
||||
func (ac AdminClientMock) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
return MinioServerInfoMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
|
||||
return minioListRemoteBucketsMock(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error) {
|
||||
return minioGetRemoteBucketMock(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removeRemoteBucket(_ context.Context, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
|
||||
return minioAddRemoteBucketMock(ctx, bucket, target)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) changePassword(ctx context.Context, accessKey, secretKey string) error {
|
||||
return minioChangePasswordMock(ctx, accessKey, secretKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) speedtest(_ context.Context, _ madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) verifyTierStatus(ctx context.Context, tier string) error {
|
||||
return minioVerifyTierStatusMock(ctx, tier)
|
||||
}
|
||||
|
||||
// mock function helpConfigKV()
|
||||
func (ac AdminClientMock) helpConfigKV(_ context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return minioHelpConfigKVMock(subSys, key, envOnly)
|
||||
}
|
||||
|
||||
// mock function getConfigKV()
|
||||
func (ac AdminClientMock) getConfigKV(_ context.Context, name string) ([]byte, error) {
|
||||
return minioGetConfigKVMock(name)
|
||||
}
|
||||
|
||||
// mock function setConfigKV()
|
||||
func (ac AdminClientMock) setConfigKV(_ context.Context, kv string) (restart bool, err error) {
|
||||
return minioSetConfigKVMock(kv)
|
||||
}
|
||||
|
||||
// mock function helpConfigKV()
|
||||
func (ac AdminClientMock) helpConfigKVGlobal(_ context.Context, envOnly bool) (madmin.Help, error) {
|
||||
return minioHelpConfigKVGlobalMock(envOnly)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) delConfigKV(_ context.Context, name string) (err error) {
|
||||
return minioDelConfigKVMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return minioGetLogsMock(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listGroups(_ context.Context) ([]string, error) {
|
||||
return minioListGroupsMock()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) updateGroupMembers(_ context.Context, req madmin.GroupAddRemove) error {
|
||||
return minioUpdateGroupMembersMock(req)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getGroupDescription(_ context.Context, group string) (*madmin.GroupDesc, error) {
|
||||
return minioGetGroupDescriptionMock(group)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setGroupStatus(_ context.Context, group string, status madmin.GroupStatus) error {
|
||||
return minioSetGroupStatusMock(group, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool,
|
||||
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
|
||||
return minioHealMock(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error) {
|
||||
return minioServerHealthInfoMock(ctx, deadline)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addOrUpdateIDPConfig(_ context.Context, _, _, _ string, _ bool) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listIDPConfig(_ context.Context, _ string) ([]madmin.IDPListItem, error) {
|
||||
return []madmin.IDPListItem{{Name: "mock"}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteIDPConfig(_ context.Context, _, _ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getIDPConfig(_ context.Context, _, _ string) (c madmin.IDPConfig, err error) {
|
||||
return madmin.IDPConfig{Info: []madmin.IDPCfgInfo{{Key: "mock", Value: "mock"}}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsStatus(_ context.Context) (madmin.KMSStatus, error) {
|
||||
return madmin.KMSStatus{Name: "name", DefaultKeyID: "key", Endpoints: map[string]madmin.ItemState{"localhost": madmin.ItemState("online")}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsAPIs(_ context.Context) ([]madmin.KMSAPI, error) {
|
||||
return []madmin.KMSAPI{{Method: "GET", Path: "/mock"}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsMetrics(_ context.Context) (*madmin.KMSMetrics, error) {
|
||||
return &madmin.KMSMetrics{}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsVersion(_ context.Context) (*madmin.KMSVersion, error) {
|
||||
return &madmin.KMSVersion{Version: "test-version"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) createKey(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listKeys(_ context.Context, _ string) ([]madmin.KMSKeyInfo, error) {
|
||||
return []madmin.KMSKeyInfo{{
|
||||
Name: "name",
|
||||
CreatedBy: "by",
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) keyStatus(_ context.Context, _ string) (*madmin.KMSKeyStatus, error) {
|
||||
return &madmin.KMSKeyStatus{KeyID: "key"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listPolicies(_ context.Context) (map[string]*iampolicy.Policy, error) {
|
||||
return minioListPoliciesMock()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getPolicy(_ context.Context, name string) (*iampolicy.Policy, error) {
|
||||
return minioGetPolicyMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removePolicy(_ context.Context, name string) error {
|
||||
return minioRemovePolicyMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addPolicy(_ context.Context, name string, policy *iampolicy.Policy) error {
|
||||
return minioAddPolicyMock(name, policy)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setPolicy(_ context.Context, policyName, entityName string, isGroup bool) error {
|
||||
return minioSetPolicyMock(policyName, entityName, isGroup)
|
||||
}
|
||||
|
||||
// mock function for startProfiling()
|
||||
func (ac AdminClientMock) startProfiling(_ context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error) {
|
||||
return minioStartProfiling(profiler, duration)
|
||||
}
|
||||
|
||||
// mock function of serviceRestart()
|
||||
func (ac AdminClientMock) serviceRestart(ctx context.Context) error {
|
||||
return minioServiceRestartMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
|
||||
return getSiteReplicationInfo(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, _ madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
|
||||
return addSiteReplicationInfo(ctx, sites)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, _ madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
|
||||
return editSiteReplicationInfo(ctx, site)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
|
||||
return deleteSiteReplicationInfoMock(ctx, removeReq)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
|
||||
return getSiteReplicationStatus(ctx, params)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
|
||||
return minioListTiersMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
|
||||
return minioTierStatsMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig) error {
|
||||
return minioAddTiersMock(ctx, tier)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removeTier(ctx context.Context, tierName string) error {
|
||||
return minioRemoveTierMock(ctx, tierName)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
|
||||
return minioEditTiersMock(ctx, tierName, creds)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
return minioServiceTraceMock(ctx, threshold, s3, internal, storage, os, errTrace)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listUsers(_ context.Context) (map[string]madmin.UserInfo, error) {
|
||||
return minioListUsersMock()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addUser(_ context.Context, accessKey, secretKey string) error {
|
||||
return minioAddUserMock(accessKey, secretKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removeUser(_ context.Context, accessKey string) error {
|
||||
return minioRemoveUserMock(accessKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getUserInfo(_ context.Context, accessKey string) (madmin.UserInfo, error) {
|
||||
return minioGetUserInfoMock(accessKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setUserStatus(_ context.Context, accessKey string, status madmin.AccountStatus) error {
|
||||
return minioSetUserStatusMock(accessKey, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
|
||||
return minioAccountInfoMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error) {
|
||||
return minioAddServiceAccountMock(ctx, policy, user, accessKey, secretKey, description, name, expiry, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
|
||||
return minioListServiceAccountsMock(ctx, user)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return minioDeleteServiceAccountMock(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
|
||||
return minioInfoServiceAccountMock(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
|
||||
return minioUpdateServiceAccountMock(ctx, serviceAccount, opts)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return minioGetLDAPPolicyEntitiesMock(ctx, query)
|
||||
}
|
||||
316
api/admin_config.go
Normal file
316
api/admin_config.go
Normal file
@@ -0,0 +1,316 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
madmin "github.com/minio/madmin-go/v3"
|
||||
|
||||
cfgApi "github.com/minio/console/api/operations/configuration"
|
||||
)
|
||||
|
||||
func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
// List Configurations
|
||||
api.ConfigurationListConfigHandler = cfgApi.ListConfigHandlerFunc(func(params cfgApi.ListConfigParams, session *models.Principal) middleware.Responder {
|
||||
configListResp, err := getListConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewListConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewListConfigOK().WithPayload(configListResp)
|
||||
})
|
||||
// Configuration Info
|
||||
api.ConfigurationConfigInfoHandler = cfgApi.ConfigInfoHandlerFunc(func(params cfgApi.ConfigInfoParams, session *models.Principal) middleware.Responder {
|
||||
config, err := getConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewConfigInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewConfigInfoOK().WithPayload(config)
|
||||
})
|
||||
// Set Configuration
|
||||
api.ConfigurationSetConfigHandler = cfgApi.SetConfigHandlerFunc(func(params cfgApi.SetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := setConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewSetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewSetConfigOK().WithPayload(resp)
|
||||
})
|
||||
// Reset Configuration
|
||||
api.ConfigurationResetConfigHandler = cfgApi.ResetConfigHandlerFunc(func(params cfgApi.ResetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := resetConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewResetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewResetConfigOK().WithPayload(resp)
|
||||
})
|
||||
// Export Configuration as base64 string.
|
||||
api.ConfigurationExportConfigHandler = cfgApi.ExportConfigHandlerFunc(func(params cfgApi.ExportConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := exportConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewExportConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewExportConfigOK().WithPayload(resp)
|
||||
})
|
||||
api.ConfigurationPostConfigsImportHandler = cfgApi.PostConfigsImportHandlerFunc(func(params cfgApi.PostConfigsImportParams, session *models.Principal) middleware.Responder {
|
||||
_, err := importConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewPostConfigsImportDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewPostConfigsImportDefault(200)
|
||||
})
|
||||
}
|
||||
|
||||
// listConfig gets all configurations' names and their descriptions
|
||||
func listConfig(client MinioAdmin) ([]*models.ConfigDescription, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
configKeysHelp, err := client.helpConfigKV(ctx, "", "", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configDescs []*models.ConfigDescription
|
||||
for _, c := range configKeysHelp.KeysHelp {
|
||||
desc := &models.ConfigDescription{
|
||||
Key: c.Key,
|
||||
Description: c.Description,
|
||||
}
|
||||
configDescs = append(configDescs, desc)
|
||||
}
|
||||
return configDescs, nil
|
||||
}
|
||||
|
||||
// getListConfigResponse performs listConfig() and serializes it to the handler's output
|
||||
func getListConfigResponse(session *models.Principal, params cfgApi.ListConfigParams) (*models.ListConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
configDescs, err := listConfig(adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
listGroupsResponse := &models.ListConfigResponse{
|
||||
Configurations: configDescs,
|
||||
Total: int64(len(configDescs)),
|
||||
}
|
||||
return listGroupsResponse, nil
|
||||
}
|
||||
|
||||
// getConfig gets the key values for a defined configuration.
|
||||
func getConfig(ctx context.Context, client MinioAdmin, name string) ([]*models.Configuration, error) {
|
||||
configBytes, err := client.getConfigKV(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subSysConfigs, err := madmin.ParseServerConfigOutput(string(configBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configSubSysList []*models.Configuration
|
||||
for _, scfg := range subSysConfigs {
|
||||
if !madmin.SubSystems.Contains(scfg.SubSystem) {
|
||||
return nil, fmt.Errorf("no sub-systems found")
|
||||
}
|
||||
var confkv []*models.ConfigurationKV
|
||||
for _, kv := range scfg.KV {
|
||||
var envOverride *models.EnvOverride
|
||||
|
||||
if kv.EnvOverride != nil {
|
||||
envOverride = &models.EnvOverride{
|
||||
Name: kv.EnvOverride.Name,
|
||||
Value: kv.EnvOverride.Value,
|
||||
}
|
||||
}
|
||||
|
||||
confkv = append(confkv, &models.ConfigurationKV{Key: kv.Key, Value: kv.Value, EnvOverride: envOverride})
|
||||
}
|
||||
if len(confkv) == 0 {
|
||||
continue
|
||||
}
|
||||
var fullConfigName string
|
||||
if scfg.Target == "" {
|
||||
fullConfigName = scfg.SubSystem
|
||||
} else {
|
||||
fullConfigName = scfg.SubSystem + ":" + scfg.Target
|
||||
}
|
||||
configSubSysList = append(configSubSysList, &models.Configuration{KeyValues: confkv, Name: fullConfigName})
|
||||
}
|
||||
return configSubSysList, nil
|
||||
}
|
||||
|
||||
// getConfigResponse performs getConfig() and serializes it to the handler's output
|
||||
func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams) ([]*models.Configuration, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
configurations, err := getConfig(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
errorVal := ErrorWithContext(ctx, err)
|
||||
minioError := madmin.ToErrorResponse(err)
|
||||
if minioError.Code == "XMinioConfigError" {
|
||||
errorVal.Code = 404
|
||||
}
|
||||
return nil, errorVal
|
||||
}
|
||||
return configurations, nil
|
||||
}
|
||||
|
||||
// setConfig sets a configuration with the defined key values
|
||||
func setConfig(ctx context.Context, client MinioAdmin, configName *string, kvs []*models.ConfigurationKV) (restart bool, err error) {
|
||||
config := buildConfig(configName, kvs)
|
||||
restart, err = client.setConfigKV(ctx, *config)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func setConfigWithARNAccountID(ctx context.Context, client MinioAdmin, configName *string, kvs []*models.ConfigurationKV, arnAccountID string) (restart bool, err error) {
|
||||
// if arnAccountID is not empty the configuration will be treated as a notification target
|
||||
// arnAccountID will be used as an identifier for that specific target
|
||||
// docs: https://min.io/docs/minio/linux/administration/monitoring/bucket-notifications.html
|
||||
if arnAccountID != "" {
|
||||
configName = swag.String(fmt.Sprintf("%s:%s", *configName, arnAccountID))
|
||||
}
|
||||
return setConfig(ctx, client, configName, kvs)
|
||||
}
|
||||
|
||||
// buildConfig builds a concatenated string including name and keyvalues
|
||||
// e.g. `region name=us-west-1`
|
||||
func buildConfig(configName *string, kvs []*models.ConfigurationKV) *string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString(*configName)
|
||||
for _, kv := range kvs {
|
||||
key := strings.TrimSpace(kv.Key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString(key)
|
||||
builder.WriteString("=")
|
||||
// All newlines must be converted to ','
|
||||
builder.WriteString(strings.ReplaceAll(strings.TrimSpace(fmt.Sprintf("\"%s\"", kv.Value)), "\n", ","))
|
||||
}
|
||||
config := builder.String()
|
||||
return &config
|
||||
}
|
||||
|
||||
// setConfigResponse implements setConfig() to be used by handler
|
||||
func setConfigResponse(session *models.Principal, params cfgApi.SetConfigParams) (*models.SetConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
configName := params.Name
|
||||
|
||||
needsRestart, err := setConfigWithARNAccountID(ctx, adminClient, &configName, params.Body.KeyValues, params.Body.ArnResourceID)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetConfigResponse{Restart: needsRestart}, nil
|
||||
}
|
||||
|
||||
func resetConfig(ctx context.Context, client MinioAdmin, configName *string) (err error) {
|
||||
err = client.delConfigKV(ctx, *configName)
|
||||
return err
|
||||
}
|
||||
|
||||
// resetConfigResponse implements resetConfig() to be used by handler
|
||||
func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigParams) (*models.SetConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
err = resetConfig(ctx, adminClient, ¶ms.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return &models.SetConfigResponse{Restart: true}, nil
|
||||
}
|
||||
|
||||
func exportConfigResponse(session *models.Principal, params cfgApi.ExportConfigParams) (*models.ConfigExportResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
configRes, err := mAdmin.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// may contain sensitive information so unpack only when required.
|
||||
return &models.ConfigExportResponse{
|
||||
Status: "success",
|
||||
Value: base64.StdEncoding.EncodeToString(configRes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func importConfigResponse(session *models.Principal, params cfgApi.PostConfigsImportParams) (*cfgApi.PostConfigsImportDefault, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
file, _, err := params.HTTPRequest.FormFile("file")
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = mAdmin.SetConfig(ctx, file)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &cfgApi.PostConfigsImportDefault{}, nil
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -28,55 +28,21 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
NotifyPostgresSubSys = "notify_postgres"
|
||||
PostgresFormat = "format"
|
||||
PostgresConnectionString = "connection_string"
|
||||
PostgresTable = "table"
|
||||
PostgresHost = "host"
|
||||
PostgresPort = "port"
|
||||
PostgresUsername = "username"
|
||||
PostgresPassword = "password"
|
||||
PostgresDatabase = "database"
|
||||
PostgresQueueDir = "queue_dir"
|
||||
PostgresQueueLimit = "queue_limit"
|
||||
PostgresMaxOpenConnections = "max_open_connections"
|
||||
NotifyPostgresSubSys = "notify_postgres"
|
||||
PostgresFormat = "format"
|
||||
PostgresConnectionString = "connection_string"
|
||||
PostgresTable = "table"
|
||||
PostgresQueueDir = "queue_dir"
|
||||
PostgresQueueLimit = "queue_limit"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioHelpConfigKVMock func(subSys, key string, envOnly bool) (madmin.Help, error)
|
||||
|
||||
var (
|
||||
minioGetConfigKVMock func(key string) ([]byte, error)
|
||||
minioSetConfigKVMock func(kv string) (restart bool, err error)
|
||||
minioDelConfigKVMock func(name string) (err error)
|
||||
)
|
||||
|
||||
// mock function helpConfigKV()
|
||||
func (ac adminClientMock) helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return minioHelpConfigKVMock(subSys, key, envOnly)
|
||||
}
|
||||
|
||||
// mock function getConfigKV()
|
||||
func (ac adminClientMock) getConfigKV(ctx context.Context, name string) ([]byte, error) {
|
||||
return minioGetConfigKVMock(name)
|
||||
}
|
||||
|
||||
// mock function setConfigKV()
|
||||
func (ac adminClientMock) setConfigKV(ctx context.Context, kv string) (restart bool, err error) {
|
||||
return minioSetConfigKVMock(kv)
|
||||
}
|
||||
|
||||
func (ac adminClientMock) delConfigKV(ctx context.Context, name string) (err error) {
|
||||
return minioDelConfigKVMock(name)
|
||||
}
|
||||
|
||||
func TestListConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
function := "listConfig()"
|
||||
// Test-1 : listConfig() get list of two configurations and ensure is output correctly
|
||||
configListMock := []madmin.HelpKV{
|
||||
@@ -97,7 +63,7 @@ func TestListConfig(t *testing.T) {
|
||||
}
|
||||
expectedKeysDesc := mockConfigList.KeysHelp
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
configList, err := listConfig(adminClient)
|
||||
@@ -114,7 +80,7 @@ func TestListConfig(t *testing.T) {
|
||||
|
||||
// Test-2 : listConfig() Return error and see that the error is handled correctly and returned
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return madmin.Help{}, errors.New("error")
|
||||
}
|
||||
_, err = listConfig(adminClient)
|
||||
@@ -125,10 +91,10 @@ func TestListConfig(t *testing.T) {
|
||||
|
||||
func TestSetConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setConfig()"
|
||||
// mock function response from setConfig()
|
||||
minioSetConfigKVMock = func(kv string) (restart bool, err error) {
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
configName := "notify_postgres"
|
||||
@@ -153,7 +119,7 @@ func TestSetConfig(t *testing.T) {
|
||||
assert.Equal(restart, false)
|
||||
|
||||
// Test-2 : setConfig() returns error, handle properly
|
||||
minioSetConfigKVMock = func(kv string) (restart bool, err error) {
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
}
|
||||
restart, err = setConfig(ctx, adminClient, &configName, kvs)
|
||||
@@ -163,7 +129,7 @@ func TestSetConfig(t *testing.T) {
|
||||
assert.Equal(restart, false)
|
||||
|
||||
// Test-4 : setConfig() set config, need restart
|
||||
minioSetConfigKVMock = func(kv string) (restart bool, err error) {
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
restart, err = setConfig(ctx, adminClient, &configName, kvs)
|
||||
@@ -175,10 +141,10 @@ func TestSetConfig(t *testing.T) {
|
||||
|
||||
func TestDelConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
function := "resetConfig()"
|
||||
// mock function response from setConfig()
|
||||
minioDelConfigKVMock = func(name string) (err error) {
|
||||
minioDelConfigKVMock = func(_ string) (err error) {
|
||||
return nil
|
||||
}
|
||||
configName := "region"
|
||||
@@ -192,7 +158,7 @@ func TestDelConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-2 : resetConfig() returns error, handle properly
|
||||
minioDelConfigKVMock = func(name string) (err error) {
|
||||
minioDelConfigKVMock = func(_ string) (err error) {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -254,7 +220,7 @@ func Test_buildConfig(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
if got := buildConfig(tt.args.configName, tt.args.kvs); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildConfig() = %s, want %s", *got, *tt.want)
|
||||
}
|
||||
@@ -264,7 +230,7 @@ func Test_buildConfig(t *testing.T) {
|
||||
|
||||
func Test_setConfigWithARN(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
client := adminClientMock{}
|
||||
client := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -294,7 +260,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
},
|
||||
arn: "1",
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -314,7 +280,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
},
|
||||
arn: "1",
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -334,7 +300,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
},
|
||||
arn: "",
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
wantErr: false,
|
||||
@@ -354,7 +320,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
},
|
||||
arn: "",
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -362,7 +328,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
// mock function response from setConfig()
|
||||
minioSetConfigKVMock = tt.mockSetConfig
|
||||
restart, err := setConfigWithARNAccountID(tt.args.ctx, tt.args.client, tt.args.configName, tt.args.kvs, tt.args.arn)
|
||||
@@ -375,7 +341,7 @@ func Test_setConfigWithARN(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_getConfig(t *testing.T) {
|
||||
client := adminClientMock{}
|
||||
client := AdminClientMock{}
|
||||
type args struct {
|
||||
client MinioAdmin
|
||||
name string
|
||||
@@ -395,7 +361,7 @@ func Test_getConfig(t *testing.T) {
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return []byte(`notify_postgres:_ connection_string="host=localhost dbname=minio_events user=postgres password=password port=5432 sslmode=disable" table=bucketevents`), nil
|
||||
}
|
||||
|
||||
@@ -441,7 +407,7 @@ func Test_getConfig(t *testing.T) {
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
@@ -469,7 +435,7 @@ func Test_getConfig(t *testing.T) {
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return []byte(`notify_postgres:_`), nil
|
||||
}
|
||||
|
||||
@@ -515,7 +481,7 @@ func Test_getConfig(t *testing.T) {
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
@@ -530,7 +496,7 @@ func Test_getConfig(t *testing.T) {
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
x := make(map[string]string)
|
||||
x["x"] = "x"
|
||||
j, _ := json.Marshal(x)
|
||||
@@ -579,7 +545,7 @@ func Test_getConfig(t *testing.T) {
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
@@ -594,13 +560,13 @@ func Test_getConfig(t *testing.T) {
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return nil, errors.New("invalid config")
|
||||
}
|
||||
|
||||
mockConfigList := madmin.Help{}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
@@ -615,11 +581,11 @@ func Test_getConfig(t *testing.T) {
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return nil, errors.New("invalid config")
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return madmin.Help{}, errors.New("no help")
|
||||
}
|
||||
},
|
||||
@@ -629,7 +595,7 @@ func Test_getConfig(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt.mock()
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
got, err := getConfig(context.Background(), tt.args.client, tt.args.name)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
const logTimeFormat string = "15:04:05 MST 01/02/2006"
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -22,21 +22,13 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioGetLogsMock func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
|
||||
// mock function of listPolicies()
|
||||
func (ac adminClientMock) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return minioGetLogsMock(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
func TestAdminConsoleLog(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startConsoleLog(ctx, )"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -48,7 +40,7 @@ func TestAdminConsoleLog(t *testing.T) {
|
||||
|
||||
// Test-1: Serve Console with no errors until Console finishes sending
|
||||
// define mock function behavior for minio server Console
|
||||
minioGetLogsMock = func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
minioGetLogsMock = func(_ context.Context, _ string, _ int, _ string) <-chan madmin.LogInfo {
|
||||
ch := make(chan madmin.LogInfo)
|
||||
// Only success, start a routine to start reading line by line.
|
||||
go func(ch chan<- madmin.LogInfo) {
|
||||
@@ -66,7 +58,7 @@ func TestAdminConsoleLog(t *testing.T) {
|
||||
}
|
||||
writesCount := 1
|
||||
// mock connection WriteMessage() no error
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, data []byte) error {
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.LogInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
@@ -90,7 +82,7 @@ func TestAdminConsoleLog(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-2: if error happens while writing, return error
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return fmt.Errorf("error on write")
|
||||
}
|
||||
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); assert.Error(err) {
|
||||
@@ -99,7 +91,7 @@ func TestAdminConsoleLog(t *testing.T) {
|
||||
|
||||
// Test-3: error happens on GetLogs Minio, Console should stop
|
||||
// and error shall be returned.
|
||||
minioGetLogsMock = func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
minioGetLogsMock = func(_ context.Context, _ string, _ int, _ string) <-chan madmin.LogInfo {
|
||||
ch := make(chan madmin.LogInfo)
|
||||
// Only success, start a routine to start reading line by line.
|
||||
go func(ch chan<- madmin.LogInfo) {
|
||||
@@ -116,7 +108,7 @@ func TestAdminConsoleLog(t *testing.T) {
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); assert.Error(err) {
|
||||
@@ -14,18 +14,17 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
groupApi "github.com/minio/console/restapi/operations/group"
|
||||
groupApi "github.com/minio/console/api/operations/group"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
@@ -35,7 +34,7 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.GroupListGroupsHandler = groupApi.ListGroupsHandlerFunc(func(params groupApi.ListGroupsParams, session *models.Principal) middleware.Responder {
|
||||
listGroupsResponse, err := getListGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return groupApi.NewListGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
return groupApi.NewListGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewListGroupsOK().WithPayload(listGroupsResponse)
|
||||
})
|
||||
@@ -43,21 +42,21 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.GroupGroupInfoHandler = groupApi.GroupInfoHandlerFunc(func(params groupApi.GroupInfoParams, session *models.Principal) middleware.Responder {
|
||||
groupInfo, err := getGroupInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return groupApi.NewGroupInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return groupApi.NewGroupInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewGroupInfoOK().WithPayload(groupInfo)
|
||||
})
|
||||
// Add Group
|
||||
api.GroupAddGroupHandler = groupApi.AddGroupHandlerFunc(func(params groupApi.AddGroupParams, session *models.Principal) middleware.Responder {
|
||||
if err := getAddGroupResponse(session, params); err != nil {
|
||||
return groupApi.NewAddGroupDefault(int(err.Code)).WithPayload(err)
|
||||
return groupApi.NewAddGroupDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewAddGroupCreated()
|
||||
})
|
||||
// Remove Group
|
||||
api.GroupRemoveGroupHandler = groupApi.RemoveGroupHandlerFunc(func(params groupApi.RemoveGroupParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRemoveGroupResponse(session, params); err != nil {
|
||||
return groupApi.NewRemoveGroupDefault(int(err.Code)).WithPayload(err)
|
||||
return groupApi.NewRemoveGroupDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewRemoveGroupNoContent()
|
||||
})
|
||||
@@ -65,17 +64,17 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.GroupUpdateGroupHandler = groupApi.UpdateGroupHandlerFunc(func(params groupApi.UpdateGroupParams, session *models.Principal) middleware.Responder {
|
||||
groupUpdateResp, err := getUpdateGroupResponse(session, params)
|
||||
if err != nil {
|
||||
return groupApi.NewUpdateGroupDefault(int(err.Code)).WithPayload(err)
|
||||
return groupApi.NewUpdateGroupDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewUpdateGroupOK().WithPayload(groupUpdateResp)
|
||||
})
|
||||
}
|
||||
|
||||
// getListGroupsResponse performs listGroups() and serializes it to the handler's output
|
||||
func getListGroupsResponse(session *models.Principal, params groupApi.ListGroupsParams) (*models.ListGroupsResponse, *models.Error) {
|
||||
func getListGroupsResponse(session *models.Principal, params groupApi.ListGroupsParams) (*models.ListGroupsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -107,10 +106,10 @@ func groupInfo(ctx context.Context, client MinioAdmin, group string) (*madmin.Gr
|
||||
}
|
||||
|
||||
// getGroupInfoResponse performs groupInfo() and serializes it to the handler's output
|
||||
func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoParams) (*models.Group, *models.Error) {
|
||||
func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoParams) (*models.Group, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -118,12 +117,7 @@ func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoPa
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groupName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
groupDesc, err := groupInfo(ctx, adminClient, groupName)
|
||||
groupDesc, err := groupInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -153,7 +147,7 @@ func addGroup(ctx context.Context, client MinioAdmin, group string, members []st
|
||||
}
|
||||
|
||||
// getAddGroupResponse performs addGroup() and serializes it to the handler's output
|
||||
func getAddGroupResponse(session *models.Principal, params groupApi.AddGroupParams) *models.Error {
|
||||
func getAddGroupResponse(session *models.Principal, params groupApi.AddGroupParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// AddGroup request needed to proceed
|
||||
@@ -161,7 +155,7 @@ func getAddGroupResponse(session *models.Principal, params groupApi.AddGroupPara
|
||||
return ErrorWithContext(ctx, ErrGroupBodyNotInRequest)
|
||||
}
|
||||
groupRequest := params.Body
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -198,13 +192,13 @@ func removeGroup(ctx context.Context, client MinioAdmin, group string) error {
|
||||
}
|
||||
|
||||
// getRemoveGroupResponse performs removeGroup() and serializes it to the handler's output
|
||||
func getRemoveGroupResponse(session *models.Principal, params groupApi.RemoveGroupParams) *models.Error {
|
||||
func getRemoveGroupResponse(session *models.Principal, params groupApi.RemoveGroupParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return ErrorWithContext(ctx, ErrGroupNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -212,12 +206,7 @@ func getRemoveGroupResponse(session *models.Principal, params groupApi.RemoveGro
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groupName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
if err := removeGroup(ctx, adminClient, groupName); err != nil {
|
||||
if err := removeGroup(ctx, adminClient, params.Name); err != nil {
|
||||
minioError := madmin.ToErrorResponse(err)
|
||||
err2 := ErrorWithContext(ctx, err)
|
||||
if minioError.Code == "XMinioAdminNoSuchGroup" {
|
||||
@@ -282,7 +271,7 @@ func setGroupStatus(ctx context.Context, client MinioAdmin, group, status string
|
||||
// getUpdateGroupResponse updates a group by adding or removing it's members depending on the request,
|
||||
// also sets the group's status if status in the request is different than the current one.
|
||||
// Then serializes the output to be used by the handler.
|
||||
func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGroupParams) (*models.Group, *models.Error) {
|
||||
func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGroupParams) (*models.Group, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
@@ -293,12 +282,7 @@ func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGro
|
||||
}
|
||||
expectedGroupUpdate := params.Body
|
||||
|
||||
groupName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -306,7 +290,7 @@ func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGro
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, params.Name, expectedGroupUpdate)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -24,42 +24,13 @@ import (
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioListGroupsMock func() ([]string, error)
|
||||
|
||||
var (
|
||||
minioUpdateGroupMembersMock func(madmin.GroupAddRemove) error
|
||||
minioGetGroupDescriptionMock func(group string) (*madmin.GroupDesc, error)
|
||||
minioSetGroupStatusMock func(group string, status madmin.GroupStatus) error
|
||||
)
|
||||
|
||||
// mock function of listGroups()
|
||||
func (ac adminClientMock) listGroups(ctx context.Context) ([]string, error) {
|
||||
return minioListGroupsMock()
|
||||
}
|
||||
|
||||
// mock function of updateGroupMembers()
|
||||
func (ac adminClientMock) updateGroupMembers(ctx context.Context, req madmin.GroupAddRemove) error {
|
||||
return minioUpdateGroupMembersMock(req)
|
||||
}
|
||||
|
||||
// mock function of getGroupDescription()
|
||||
func (ac adminClientMock) getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error) {
|
||||
return minioGetGroupDescriptionMock(group)
|
||||
}
|
||||
|
||||
// mock function setGroupStatus()
|
||||
func (ac adminClientMock) setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error {
|
||||
return minioSetGroupStatusMock(group, status)
|
||||
}
|
||||
|
||||
func TestListGroups(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : listGroups() Get response from minio client with two Groups and return the same number on listGroups()
|
||||
@@ -95,7 +66,7 @@ func TestListGroups(t *testing.T) {
|
||||
|
||||
func TestAddGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : addGroup() add a new group with two members
|
||||
@@ -122,7 +93,7 @@ func TestAddGroup(t *testing.T) {
|
||||
|
||||
func TestRemoveGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : removeGroup() remove group assume it has no members
|
||||
@@ -147,7 +118,7 @@ func TestRemoveGroup(t *testing.T) {
|
||||
|
||||
func TestGroupInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : groupInfo() get group info
|
||||
@@ -159,7 +130,7 @@ func TestGroupInfo(t *testing.T) {
|
||||
Status: "enabled",
|
||||
}
|
||||
// mock function response from updateGroupMembers()
|
||||
minioGetGroupDescriptionMock = func(group string) (*madmin.GroupDesc, error) {
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
function := "groupInfo()"
|
||||
@@ -173,7 +144,7 @@ func TestGroupInfo(t *testing.T) {
|
||||
assert.Equal("enabled", info.Status)
|
||||
|
||||
// Test-2 : groupInfo() Return error and see that the error is handled correctly and returned
|
||||
minioGetGroupDescriptionMock = func(group string) (*madmin.GroupDesc, error) {
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
_, err = groupInfo(ctx, adminClient, groupName)
|
||||
@@ -184,7 +155,7 @@ func TestGroupInfo(t *testing.T) {
|
||||
|
||||
func TestUpdateGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : addOrDeleteMembers() update group members add user3 and delete user2
|
||||
@@ -255,7 +226,7 @@ func TestUpdateGroup(t *testing.T) {
|
||||
// the function twice but the second time returned an error
|
||||
is2ndRunGroupInfo := false
|
||||
// mock function response from updateGroupMembers()
|
||||
minioGetGroupDescriptionMock = func(group string) (*madmin.GroupDesc, error) {
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
if is2ndRunGroupInfo {
|
||||
return mockResponseAfterUpdate, nil
|
||||
}
|
||||
@@ -265,7 +236,7 @@ func TestUpdateGroup(t *testing.T) {
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
minioSetGroupStatusMock = func(group string, status madmin.GroupStatus) error {
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
@@ -280,14 +251,14 @@ func TestUpdateGroup(t *testing.T) {
|
||||
|
||||
func TestSetGroupStatus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setGroupStatus()"
|
||||
groupName := "acmeGroup"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: setGroupStatus() update valid disabled status
|
||||
expectedStatus := "disabled"
|
||||
minioSetGroupStatusMock = func(group string, status madmin.GroupStatus) error {
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); err != nil {
|
||||
@@ -295,7 +266,7 @@ func TestSetGroupStatus(t *testing.T) {
|
||||
}
|
||||
// Test-2: setGroupStatus() update valid enabled status
|
||||
expectedStatus = "enabled"
|
||||
minioSetGroupStatusMock = func(group string, status madmin.GroupStatus) error {
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); err != nil {
|
||||
@@ -303,7 +274,7 @@ func TestSetGroupStatus(t *testing.T) {
|
||||
}
|
||||
// Test-3: setGroupStatus() update invalid status, should send error
|
||||
expectedStatus = "invalid"
|
||||
minioSetGroupStatusMock = func(group string, status madmin.GroupStatus) error {
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); assert.Error(err) {
|
||||
@@ -311,7 +282,7 @@ func TestSetGroupStatus(t *testing.T) {
|
||||
}
|
||||
// Test-4: setGroupStatus() handler error correctly
|
||||
expectedStatus = "enabled"
|
||||
minioSetGroupStatusMock = func(group string, status madmin.GroupStatus) error {
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); assert.Error(err) {
|
||||
290
api/admin_idp.go
Normal file
290
api/admin_idp.go
Normal file
@@ -0,0 +1,290 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/api/operations/idp"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
var errInvalidIDPType = fmt.Errorf("IDP type must be one of %v", madmin.ValidIDPConfigTypes)
|
||||
|
||||
func registerIDPHandlers(api *operations.ConsoleAPI) {
|
||||
api.IdpCreateConfigurationHandler = idp.CreateConfigurationHandlerFunc(func(params idp.CreateConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := createIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewCreateConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewCreateConfigurationCreated().WithPayload(response)
|
||||
})
|
||||
api.IdpUpdateConfigurationHandler = idp.UpdateConfigurationHandlerFunc(func(params idp.UpdateConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := updateIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewUpdateConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewUpdateConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpListConfigurationsHandler = idp.ListConfigurationsHandlerFunc(func(params idp.ListConfigurationsParams, session *models.Principal) middleware.Responder {
|
||||
response, err := listIDPConfigurationsResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewListConfigurationsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewListConfigurationsOK().WithPayload(response)
|
||||
})
|
||||
api.IdpDeleteConfigurationHandler = idp.DeleteConfigurationHandlerFunc(func(params idp.DeleteConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := deleteIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewDeleteConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewDeleteConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpGetConfigurationHandler = idp.GetConfigurationHandlerFunc(func(params idp.GetConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getIDPConfigurationsResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewGetConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewGetConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpGetLDAPEntitiesHandler = idp.GetLDAPEntitiesHandlerFunc(func(params idp.GetLDAPEntitiesParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getLDAPEntitiesResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewGetLDAPEntitiesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewGetLDAPEntitiesOK().WithPayload(response)
|
||||
})
|
||||
}
|
||||
|
||||
func createIDPConfigurationResponse(session *models.Principal, params idp.CreateConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := createOrUpdateIDPConfig(ctx, params.Type, params.Body.Name, params.Body.Input, false, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func updateIDPConfigurationResponse(session *models.Principal, params idp.UpdateConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := createOrUpdateIDPConfig(ctx, params.Type, params.Name, params.Body.Input, true, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func createOrUpdateIDPConfig(ctx context.Context, idpType, name, input string, update bool, client MinioAdmin) (bool, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return false, errInvalidIDPType
|
||||
}
|
||||
restart, err := client.addOrUpdateIDPConfig(ctx, idpType, name, input, update)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func listIDPConfigurationsResponse(session *models.Principal, params idp.ListConfigurationsParams) (*models.IdpListConfigurationsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
results, err := listIDPConfigurations(ctx, params.Type, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.IdpListConfigurationsResponse{Results: results}, nil
|
||||
}
|
||||
|
||||
func listIDPConfigurations(ctx context.Context, idpType string, client MinioAdmin) ([]*models.IdpServerConfiguration, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return nil, errInvalidIDPType
|
||||
}
|
||||
results, err := client.listIDPConfig(ctx, idpType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseIDPConfigurations(results), nil
|
||||
}
|
||||
|
||||
func parseIDPConfigurations(configs []madmin.IDPListItem) (serverConfigs []*models.IdpServerConfiguration) {
|
||||
for _, c := range configs {
|
||||
serverConfigs = append(serverConfigs, &models.IdpServerConfiguration{
|
||||
Name: c.Name,
|
||||
Enabled: c.Enabled,
|
||||
Type: c.Type,
|
||||
})
|
||||
}
|
||||
return serverConfigs
|
||||
}
|
||||
|
||||
func deleteIDPConfigurationResponse(session *models.Principal, params idp.DeleteConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := deleteIDPConfig(ctx, params.Type, params.Name, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func deleteIDPConfig(ctx context.Context, idpType, name string, client MinioAdmin) (bool, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return false, errInvalidIDPType
|
||||
}
|
||||
restart, err := client.deleteIDPConfig(ctx, idpType, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func getIDPConfigurationsResponse(session *models.Principal, params idp.GetConfigurationParams) (*models.IdpServerConfiguration, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
result, err := getIDPConfiguration(ctx, params.Type, params.Name, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getIDPConfiguration(ctx context.Context, idpType, name string, client MinioAdmin) (*models.IdpServerConfiguration, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return nil, errInvalidIDPType
|
||||
}
|
||||
config, err := client.getIDPConfig(ctx, idpType, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.IdpServerConfiguration{
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
Info: parseIDPConfigurationsInfo(config.Info),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseIDPConfigurationsInfo(infoList []madmin.IDPCfgInfo) (results []*models.IdpServerConfigurationInfo) {
|
||||
for _, info := range infoList {
|
||||
results = append(results, &models.IdpServerConfigurationInfo{
|
||||
Key: info.Key,
|
||||
Value: info.Value,
|
||||
IsCfg: info.IsCfg,
|
||||
IsEnv: info.IsEnv,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getLDAPEntitiesResponse(session *models.Principal, params idp.GetLDAPEntitiesParams) (*models.LdapEntities, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
result, err := getEntitiesResult(ctx, AdminClient{Client: mAdmin}, params.Body.Users, params.Body.Groups, params.Body.Policies)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getEntitiesResult(ctx context.Context, client MinioAdmin, users, groups, policies []string) (*models.LdapEntities, error) {
|
||||
entities, err := client.getLDAPPolicyEntities(ctx, madmin.PolicyEntitiesQuery{
|
||||
Users: users,
|
||||
Groups: groups,
|
||||
Policy: policies,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result models.LdapEntities
|
||||
|
||||
var usersEntity []*models.LdapUserPolicyEntity
|
||||
var groupsEntity []*models.LdapGroupPolicyEntity
|
||||
var policiesEntity []*models.LdapPolicyEntity
|
||||
|
||||
result.Timestamp = entities.Timestamp.Format(time.RFC3339)
|
||||
|
||||
for _, userMapping := range entities.UserMappings {
|
||||
mapItem := models.LdapUserPolicyEntity{
|
||||
User: userMapping.User,
|
||||
Policies: userMapping.Policies,
|
||||
}
|
||||
|
||||
usersEntity = append(usersEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Users = usersEntity
|
||||
|
||||
for _, groupsMapping := range entities.GroupMappings {
|
||||
mapItem := models.LdapGroupPolicyEntity{
|
||||
Group: groupsMapping.Group,
|
||||
Policies: groupsMapping.Policies,
|
||||
}
|
||||
|
||||
groupsEntity = append(groupsEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Groups = groupsEntity
|
||||
|
||||
for _, policyMapping := range entities.PolicyMappings {
|
||||
mapItem := models.LdapPolicyEntity{
|
||||
Policy: policyMapping.Policy,
|
||||
Users: policyMapping.Users,
|
||||
Groups: policyMapping.Groups,
|
||||
}
|
||||
|
||||
policiesEntity = append(policiesEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Policies = policiesEntity
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
319
api/admin_idp_test.go
Normal file
319
api/admin_idp_test.go
Normal file
@@ -0,0 +1,319 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/api/operations/idp"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type IDPTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestRegisterIDPHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerIDPHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.IdpCreateConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpListConfigurationsHandler)
|
||||
suite.assert.Nil(api.IdpUpdateConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpDeleteConfigurationHandler)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.IdpCreateConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpListConfigurationsHandler)
|
||||
suite.assert.NotNil(api.IdpUpdateConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpDeleteConfigurationHandler)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initCreateIDPConfigurationRequest()
|
||||
response := api.IdpCreateConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.CreateConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initCreateIDPConfigurationRequest() (params idp.CreateConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.IdpServerConfiguration{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "ldap", "", "", false, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "", "", "", false, suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initUpdateIDPConfigurationRequest()
|
||||
response := api.IdpUpdateConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.UpdateConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initUpdateIDPConfigurationRequest() (params idp.UpdateConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.IdpServerConfiguration{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "ldap", "", "", true, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "", "", "", true, suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initListIDPConfigurationsRequest()
|
||||
response := api.IdpListConfigurationsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.ListConfigurationsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initListIDPConfigurationsRequest() (params idp.ListConfigurationsParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationsWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := listIDPConfigurations(ctx, "ldap", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationsWithWrongType() {
|
||||
ctx := context.Background()
|
||||
res, err := listIDPConfigurations(ctx, "", suite.adminClient)
|
||||
suite.assert.Nil(res)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initDeleteIDPConfigurationRequest()
|
||||
response := api.IdpDeleteConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.DeleteConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initDeleteIDPConfigurationRequest() (params idp.DeleteConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := deleteIDPConfig(ctx, "ldap", "", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := deleteIDPConfig(ctx, "", "", suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initGetIDPConfigurationRequest()
|
||||
response := api.IdpGetConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.GetConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initGetIDPConfigurationRequest() (params idp.GetConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := getIDPConfiguration(ctx, "ldap", "", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
res, err := getIDPConfiguration(ctx, "", "", suite.adminClient)
|
||||
suite.assert.Nil(res)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func TestIDP(t *testing.T) {
|
||||
suite.Run(t, new(IDPTestSuite))
|
||||
}
|
||||
|
||||
func TestGetEntitiesResult(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
client := AdminClientMock{}
|
||||
function := "getEntitiesResult()"
|
||||
|
||||
usersList := []string{"user1", "user2", "user3"}
|
||||
policiesList := []string{"policy1", "policy2", "policy3"}
|
||||
groupsList := []string{"group1", "group3", "group5"}
|
||||
|
||||
policyMap := []madmin.PolicyEntities{
|
||||
{Policy: "testPolicy0", Groups: groupsList, Users: usersList},
|
||||
{Policy: "testPolicy1", Groups: groupsList, Users: usersList},
|
||||
}
|
||||
|
||||
usersMap := []madmin.UserPolicyEntities{
|
||||
{User: "testUser0", Policies: policiesList},
|
||||
{User: "testUser1", Policies: policiesList},
|
||||
}
|
||||
|
||||
groupsMap := []madmin.GroupPolicyEntities{
|
||||
{Group: "group0", Policies: policiesList},
|
||||
{Group: "group1", Policies: policiesList},
|
||||
}
|
||||
|
||||
// Test-1: getEntitiesResult list all information provided
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
mockResponse := madmin.PolicyEntitiesResult{
|
||||
PolicyMappings: policyMap,
|
||||
GroupMappings: groupsMap,
|
||||
UserMappings: usersMap,
|
||||
}
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
|
||||
entities, err := getEntitiesResult(ctx, client, usersList, groupsList, policiesList)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
for i, groupIt := range entities.Groups {
|
||||
assert.Equal(fmt.Sprintf("group%d", i), groupIt.Group)
|
||||
|
||||
for i, polItm := range groupIt.Policies {
|
||||
assert.Equal(policiesList[i], polItm)
|
||||
}
|
||||
}
|
||||
|
||||
for i, usrIt := range entities.Users {
|
||||
assert.Equal(fmt.Sprintf("testUser%d", i), usrIt.User)
|
||||
|
||||
for i, polItm := range usrIt.Policies {
|
||||
assert.Equal(policiesList[i], polItm)
|
||||
}
|
||||
}
|
||||
|
||||
for i, policyIt := range entities.Policies {
|
||||
assert.Equal(fmt.Sprintf("testPolicy%d", i), policyIt.Policy)
|
||||
|
||||
for i, userItm := range policyIt.Users {
|
||||
assert.Equal(usersList[i], userItm)
|
||||
}
|
||||
|
||||
for i, grItm := range policyIt.Groups {
|
||||
assert.Equal(groupsList[i], grItm)
|
||||
}
|
||||
}
|
||||
|
||||
// Test-2: getEntitiesResult error is returned from getLDAPPolicyEntities()
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return madmin.PolicyEntitiesResult{}, errors.New("error")
|
||||
}
|
||||
|
||||
_, err = getEntitiesResult(ctx, client, usersList, groupsList, policiesList)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
1192
api/admin_info.go
Normal file
1192
api/admin_info.go
Normal file
File diff suppressed because it is too large
Load Diff
152
api/admin_info_test.go
Normal file
152
api/admin_info_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
systemApi "github.com/minio/console/api/operations/system"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type AdminInfoTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
isPrometheusRequest bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{
|
||||
Servers: []madmin.ServerProperties{{
|
||||
Disks: []madmin.Disk{{}},
|
||||
}},
|
||||
Backend: madmin.ErasureBackend{Type: "mock"},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
if suite.isPrometheusRequest {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestRegisterAdminInfoHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerAdminInfoHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.SystemAdminInfoHandler)
|
||||
suite.assert.Nil(api.SystemDashboardWidgetDetailsHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.SystemAdminInfoHandler)
|
||||
suite.assert.NotNil(api.SystemDashboardWidgetDetailsHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestSystemAdminInfoHandlerWithError() {
|
||||
params, api := suite.initSystemAdminInfoRequest()
|
||||
response := api.SystemAdminInfoHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*systemApi.AdminInfoDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) initSystemAdminInfoRequest() (params systemApi.AdminInfoParams, api operations.ConsoleAPI) {
|
||||
registerAdminInfoHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
defaultOnly := false
|
||||
params.DefaultOnly = &defaultOnly
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestSystemDashboardWidgetDetailsHandlerWithError() {
|
||||
params, api := suite.initSystemDashboardWidgetDetailsRequest()
|
||||
response := api.SystemDashboardWidgetDetailsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*systemApi.DashboardWidgetDetailsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) initSystemDashboardWidgetDetailsRequest() (params systemApi.DashboardWidgetDetailsParams, api operations.ConsoleAPI) {
|
||||
registerAdminInfoHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetUsageWidgetsForDeploymentWithoutError() {
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
suite.isPrometheusRequest = true
|
||||
res, err := getUsageWidgetsForDeployment(ctx, suite.server.URL, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.isPrometheusRequest = false
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetWidgetDetailsWithoutError() {
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
suite.isPrometheusRequest = true
|
||||
var step int32 = 1
|
||||
var start int64
|
||||
var end int64 = 1
|
||||
res, err := getWidgetDetails(ctx, suite.server.URL, "mock", 1, &step, &start, &end)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.isPrometheusRequest = false
|
||||
}
|
||||
|
||||
func TestAdminInfo(t *testing.T) {
|
||||
suite.Run(t, new(AdminInfoTestSuite))
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -22,39 +22,30 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
inspectApi "github.com/minio/console/api/operations/inspect"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
inspectApi "github.com/minio/console/restapi/operations/inspect"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/secure-io/sio-go"
|
||||
)
|
||||
|
||||
func registerInspectHandler(api *operations.ConsoleAPI) {
|
||||
api.InspectInspectHandler = inspectApi.InspectHandlerFunc(func(params inspectApi.InspectParams, principal *models.Principal) middleware.Responder {
|
||||
if v, err := base64.URLEncoding.DecodeString(params.File); err == nil && utf8.Valid(v) {
|
||||
params.File = string(v)
|
||||
}
|
||||
|
||||
if v, err := base64.URLEncoding.DecodeString(params.Volume); err == nil && utf8.Valid(v) {
|
||||
params.Volume = string(v)
|
||||
}
|
||||
|
||||
k, r, err := getInspectResult(principal, ¶ms)
|
||||
if err != nil {
|
||||
return inspectApi.NewInspectDefault(int(err.Code)).WithPayload(err)
|
||||
return inspectApi.NewInspectDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return middleware.ResponderFunc(processInspectResponse(¶ms, k, r))
|
||||
})
|
||||
}
|
||||
|
||||
func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) ([]byte, io.ReadCloser, *models.Error) {
|
||||
func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) ([]byte, io.ReadCloser, *CodedAPIError) {
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -120,7 +111,7 @@ func processInspectResponse(params *inspectApi.InspectParams, k []byte, r io.Rea
|
||||
|
||||
_, err := io.Copy(w, r)
|
||||
if err != nil {
|
||||
LogError("Unable to write all the data: %v", err)
|
||||
LogError("unable to write all the data: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
296
api/admin_kms.go
Normal file
296
api/admin_kms.go
Normal file
@@ -0,0 +1,296 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
kmsAPI "github.com/minio/console/api/operations/k_m_s"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func registerKMSHandlers(api *operations.ConsoleAPI) {
|
||||
registerKMSStatusHandlers(api)
|
||||
registerKMSKeyHandlers(api)
|
||||
}
|
||||
|
||||
func registerKMSStatusHandlers(api *operations.ConsoleAPI) {
|
||||
api.KmsKMSStatusHandler = kmsAPI.KMSStatusHandlerFunc(func(params kmsAPI.KMSStatusParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSStatusResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSStatusDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSStatusOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSMetricsHandler = kmsAPI.KMSMetricsHandlerFunc(func(params kmsAPI.KMSMetricsParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSMetricsResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSMetricsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSMetricsOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSAPIsHandler = kmsAPI.KMSAPIsHandlerFunc(func(params kmsAPI.KMSAPIsParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSAPIsResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSAPIsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSAPIsOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSVersionHandler = kmsAPI.KMSVersionHandlerFunc(func(params kmsAPI.KMSVersionParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSVersionResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSVersionDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSVersionOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetKMSStatusResponse(session *models.Principal, params kmsAPI.KMSStatusParams) (*models.KmsStatusResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return kmsStatus(ctx, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func kmsStatus(ctx context.Context, minioClient MinioAdmin) (*models.KmsStatusResponse, *CodedAPIError) {
|
||||
st, err := minioClient.kmsStatus(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsStatusResponse{
|
||||
DefaultKeyID: st.DefaultKeyID,
|
||||
Name: st.Name,
|
||||
Endpoints: parseStatusEndpoints(st.Endpoints),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseStatusEndpoints(endpoints map[string]madmin.ItemState) (kmsEndpoints []*models.KmsEndpoint) {
|
||||
for key, value := range endpoints {
|
||||
kmsEndpoints = append(kmsEndpoints, &models.KmsEndpoint{URL: key, Status: string(value)})
|
||||
}
|
||||
return kmsEndpoints
|
||||
}
|
||||
|
||||
func GetKMSMetricsResponse(session *models.Principal, params kmsAPI.KMSMetricsParams) (*models.KmsMetricsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return kmsMetrics(ctx, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func kmsMetrics(ctx context.Context, minioClient MinioAdmin) (*models.KmsMetricsResponse, *CodedAPIError) {
|
||||
metrics, err := minioClient.kmsMetrics(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsMetricsResponse{
|
||||
RequestOK: &metrics.RequestOK,
|
||||
RequestErr: &metrics.RequestErr,
|
||||
RequestFail: &metrics.RequestFail,
|
||||
RequestActive: &metrics.RequestActive,
|
||||
AuditEvents: &metrics.AuditEvents,
|
||||
ErrorEvents: &metrics.ErrorEvents,
|
||||
LatencyHistogram: parseHistogram(metrics.LatencyHistogram),
|
||||
Uptime: &metrics.UpTime,
|
||||
Cpus: &metrics.CPUs,
|
||||
UsableCPUs: &metrics.UsableCPUs,
|
||||
Threads: &metrics.Threads,
|
||||
HeapAlloc: &metrics.HeapAlloc,
|
||||
HeapObjects: metrics.HeapObjects,
|
||||
StackAlloc: &metrics.StackAlloc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseHistogram(histogram map[int64]int64) (records []*models.KmsLatencyHistogram) {
|
||||
for duration, total := range histogram {
|
||||
records = append(records, &models.KmsLatencyHistogram{Duration: duration, Total: total})
|
||||
}
|
||||
cp := func(i, j int) bool {
|
||||
return records[i].Duration < records[j].Duration
|
||||
}
|
||||
sort.Slice(records, cp)
|
||||
return records
|
||||
}
|
||||
|
||||
func GetKMSAPIsResponse(session *models.Principal, params kmsAPI.KMSAPIsParams) (*models.KmsAPIsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return kmsAPIs(ctx, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func kmsAPIs(ctx context.Context, minioClient MinioAdmin) (*models.KmsAPIsResponse, *CodedAPIError) {
|
||||
apis, err := minioClient.kmsAPIs(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsAPIsResponse{
|
||||
Results: parseApis(apis),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseApis(apis []madmin.KMSAPI) (data []*models.KmsAPI) {
|
||||
for _, api := range apis {
|
||||
data = append(data, &models.KmsAPI{
|
||||
Method: api.Method,
|
||||
Path: api.Path,
|
||||
MaxBody: api.MaxBody,
|
||||
Timeout: api.Timeout,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func GetKMSVersionResponse(session *models.Principal, params kmsAPI.KMSVersionParams) (*models.KmsVersionResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return kmsVersion(ctx, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func kmsVersion(ctx context.Context, minioClient MinioAdmin) (*models.KmsVersionResponse, *CodedAPIError) {
|
||||
version, err := minioClient.kmsVersion(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsVersionResponse{
|
||||
Version: version.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func registerKMSKeyHandlers(api *operations.ConsoleAPI) {
|
||||
api.KmsKMSCreateKeyHandler = kmsAPI.KMSCreateKeyHandlerFunc(func(params kmsAPI.KMSCreateKeyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSCreateKeyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSCreateKeyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSCreateKeyCreated()
|
||||
})
|
||||
|
||||
api.KmsKMSListKeysHandler = kmsAPI.KMSListKeysHandlerFunc(func(params kmsAPI.KMSListKeysParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSListKeysResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSListKeysDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSListKeysOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSKeyStatusHandler = kmsAPI.KMSKeyStatusHandlerFunc(func(params kmsAPI.KMSKeyStatusParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSKeyStatusResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSKeyStatusDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSKeyStatusOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetKMSCreateKeyResponse(session *models.Principal, params kmsAPI.KMSCreateKeyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return createKey(ctx, *params.Body.Key, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func createKey(ctx context.Context, key string, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.createKey(ctx, key); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetKMSListKeysResponse(session *models.Principal, params kmsAPI.KMSListKeysParams) (*models.KmsListKeysResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
pattern := ""
|
||||
if params.Pattern != nil {
|
||||
pattern = *params.Pattern
|
||||
}
|
||||
return listKeys(ctx, pattern, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func listKeys(ctx context.Context, pattern string, minioClient MinioAdmin) (*models.KmsListKeysResponse, *CodedAPIError) {
|
||||
results, err := minioClient.listKeys(ctx, pattern)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsListKeysResponse{Results: parseKeys(results)}, nil
|
||||
}
|
||||
|
||||
// printDate - human friendly formatted date.
|
||||
const (
|
||||
printDate = "2006-01-02 15:04:05 MST"
|
||||
)
|
||||
|
||||
func parseKeys(results []madmin.KMSKeyInfo) (data []*models.KmsKeyInfo) {
|
||||
for _, key := range results {
|
||||
data = append(data, &models.KmsKeyInfo{
|
||||
CreatedAt: key.CreatedAt.Format(printDate),
|
||||
CreatedBy: key.CreatedBy,
|
||||
Name: key.Name,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func GetKMSKeyStatusResponse(session *models.Principal, params kmsAPI.KMSKeyStatusParams) (*models.KmsKeyStatusResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return keyStatus(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func keyStatus(ctx context.Context, key string, minioClient MinioAdmin) (*models.KmsKeyStatusResponse, *CodedAPIError) {
|
||||
ks, err := minioClient.keyStatus(ctx, key)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsKeyStatusResponse{
|
||||
KeyID: ks.KeyID,
|
||||
EncryptionErr: ks.EncryptionErr,
|
||||
DecryptionErr: ks.DecryptionErr,
|
||||
}, nil
|
||||
}
|
||||
238
api/admin_kms_test.go
Normal file
238
api/admin_kms_test.go
Normal file
@@ -0,0 +1,238 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
kmsAPI "github.com/minio/console/api/operations/k_m_s"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type KMSTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestRegisterKMSHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerKMSHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.KmsKMSStatusHandler)
|
||||
suite.assert.Nil(api.KmsKMSMetricsHandler)
|
||||
suite.assert.Nil(api.KmsKMSAPIsHandler)
|
||||
suite.assert.Nil(api.KmsKMSVersionHandler)
|
||||
suite.assert.Nil(api.KmsKMSCreateKeyHandler)
|
||||
suite.assert.Nil(api.KmsKMSListKeysHandler)
|
||||
suite.assert.Nil(api.KmsKMSKeyStatusHandler)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.KmsKMSStatusHandler)
|
||||
suite.assert.NotNil(api.KmsKMSMetricsHandler)
|
||||
suite.assert.NotNil(api.KmsKMSAPIsHandler)
|
||||
suite.assert.NotNil(api.KmsKMSVersionHandler)
|
||||
suite.assert.NotNil(api.KmsKMSCreateKeyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSListKeysHandler)
|
||||
suite.assert.NotNil(api.KmsKMSKeyStatusHandler)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSStatusHandlerWithError() {
|
||||
params, api := suite.initKMSStatusRequest()
|
||||
response := api.KmsKMSStatusHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSStatusDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSStatusRequest() (params kmsAPI.KMSStatusParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSStatusWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := kmsStatus(ctx, suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSMetricsHandlerWithError() {
|
||||
params, api := suite.initKMSMetricsRequest()
|
||||
response := api.KmsKMSMetricsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSMetricsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSMetricsRequest() (params kmsAPI.KMSMetricsParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSMetricsWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := kmsMetrics(ctx, suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSAPIsHandlerWithError() {
|
||||
params, api := suite.initKMSAPIsRequest()
|
||||
response := api.KmsKMSAPIsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSAPIsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSAPIsRequest() (params kmsAPI.KMSAPIsParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSAPIsWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := kmsAPIs(ctx, suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSVersionHandlerWithError() {
|
||||
params, api := suite.initKMSVersionRequest()
|
||||
response := api.KmsKMSVersionHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSVersionDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSVersionRequest() (params kmsAPI.KMSVersionParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSVersionWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := kmsVersion(ctx, suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSCreateKeyHandlerWithError() {
|
||||
params, api := suite.initKMSCreateKeyRequest()
|
||||
response := api.KmsKMSCreateKeyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSCreateKeyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSCreateKeyRequest() (params kmsAPI.KMSCreateKeyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
key := "key"
|
||||
params.Body = &models.KmsCreateKeyRequest{Key: &key}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSCreateKeyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := createKey(ctx, "key", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListKeysHandlerWithError() {
|
||||
params, api := suite.initKMSListKeysRequest()
|
||||
response := api.KmsKMSListKeysHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSListKeysDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSListKeysRequest() (params kmsAPI.KMSListKeysParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListKeysWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := listKeys(ctx, "", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSKeyStatusHandlerWithError() {
|
||||
params, api := suite.initKMSKeyStatusRequest()
|
||||
response := api.KmsKMSKeyStatusHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSKeyStatusDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSKeyStatusRequest() (params kmsAPI.KMSKeyStatusParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSKeyStatusWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := keyStatus(ctx, "key", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestKMS(t *testing.T) {
|
||||
suite.Run(t, new(KMSTestSuite))
|
||||
}
|
||||
@@ -14,32 +14,32 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
systemApi "github.com/minio/console/api/operations/system"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
systemApi "github.com/minio/console/restapi/operations/system"
|
||||
)
|
||||
|
||||
func registerNodesHandler(api *operations.ConsoleAPI) {
|
||||
api.SystemListNodesHandler = systemApi.ListNodesHandlerFunc(func(params systemApi.ListNodesParams, session *models.Principal) middleware.Responder {
|
||||
listNodesResponse, err := getListNodesResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewListNodesDefault(int(err.Code)).WithPayload(err)
|
||||
return systemApi.NewListNodesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return systemApi.NewListNodesOK().WithPayload(listNodesResponse)
|
||||
})
|
||||
}
|
||||
|
||||
// getListNodesResponse returns a list of available node endpoints .
|
||||
func getListNodesResponse(session *models.Principal, params systemApi.ListNodesParams) ([]string, *models.Error) {
|
||||
func getListNodesResponse(session *models.Principal, params systemApi.ListNodesParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,16 +14,16 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
configurationApi "github.com/minio/console/api/operations/configuration"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
configurationApi "github.com/minio/console/restapi/operations/configuration"
|
||||
)
|
||||
|
||||
func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
@@ -31,7 +31,7 @@ func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationNotificationEndpointListHandler = configurationApi.NotificationEndpointListHandlerFunc(func(params configurationApi.NotificationEndpointListParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getNotificationEndpointsResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewNotificationEndpointListDefault(int(err.Code)).WithPayload(err)
|
||||
return configurationApi.NewNotificationEndpointListDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return configurationApi.NewNotificationEndpointListOK().WithPayload(notifEndpoints)
|
||||
})
|
||||
@@ -39,7 +39,7 @@ func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationAddNotificationEndpointHandler = configurationApi.AddNotificationEndpointHandlerFunc(func(params configurationApi.AddNotificationEndpointParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getAddNotificationEndpointResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewAddNotificationEndpointDefault(int(err.Code)).WithPayload(err)
|
||||
return configurationApi.NewAddNotificationEndpointDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return configurationApi.NewAddNotificationEndpointCreated().WithPayload(notifEndpoints)
|
||||
})
|
||||
@@ -73,10 +73,10 @@ func getNotificationEndpoints(ctx context.Context, client MinioAdmin) (*models.N
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getNotificationEndpointsResponse(session *models.Principal, params configurationApi.NotificationEndpointListParams) (*models.NotifEndpointResponse, *models.Error) {
|
||||
func getNotificationEndpointsResponse(session *models.Principal, params configurationApi.NotificationEndpointListParams) (*models.NotifEndpointResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -143,10 +143,10 @@ func addNotificationEndpoint(ctx context.Context, client MinioAdmin, params *con
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, *models.Error) {
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -24,12 +24,12 @@ import (
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
cfgApi "github.com/minio/console/api/operations/configuration"
|
||||
"github.com/minio/console/models"
|
||||
cfgApi "github.com/minio/console/restapi/operations/configuration"
|
||||
)
|
||||
|
||||
func Test_addNotificationEndpoint(t *testing.T) {
|
||||
client := adminClientMock{}
|
||||
client := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -61,7 +61,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -94,7 +94,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
},
|
||||
want: nil,
|
||||
@@ -118,7 +118,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -149,7 +149,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -178,7 +178,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -208,7 +208,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -240,7 +240,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -273,7 +273,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -305,7 +305,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -335,7 +335,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -365,7 +365,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -397,7 +397,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("invalid config")
|
||||
},
|
||||
want: nil,
|
||||
@@ -421,7 +421,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(kv string) (restart bool, err error) {
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
@@ -438,7 +438,7 @@ func Test_addNotificationEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
// mock function response from setConfig()
|
||||
minioSetConfigKVMock = tt.mockSetConfig
|
||||
got, err := addNotificationEndpoint(tt.args.ctx, tt.args.client, tt.args.params)
|
||||
@@ -14,11 +14,10 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
"github.com/minio/mc/cmd"
|
||||
@@ -32,7 +31,7 @@ type objectsListOpts struct {
|
||||
}
|
||||
|
||||
type ObjectsRequest struct {
|
||||
Mode string `json:"mode,nonempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
BucketName string `json:"bucket_name"`
|
||||
Prefix string `json:"prefix"`
|
||||
Date string `json:"date"`
|
||||
@@ -40,18 +39,19 @@ type ObjectsRequest struct {
|
||||
}
|
||||
|
||||
type WSResponse struct {
|
||||
RequestID int64 `json:"request_id,nonempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
RequestID int64 `json:"request_id,omitempty"`
|
||||
Error *CodedAPIError `json:"error,omitempty"`
|
||||
RequestEnd bool `json:"request_end,omitempty"`
|
||||
Prefix string `json:"prefix,omitempty"`
|
||||
BucketName string `json:"bucketName,omitempty"`
|
||||
Data []ObjectResponse `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type ObjectResponse struct {
|
||||
Name string `json:"name,nonempty"`
|
||||
LastModified string `json:"last_modified,nonempty"`
|
||||
Size int64 `json:"size,nonempty"`
|
||||
VersionID string `json:"version_id,nonempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
LastModified string `json:"last_modified,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
VersionID string `json:"version_id,omitempty"`
|
||||
DeleteMarker bool `json:"delete_flag,omitempty"`
|
||||
IsLatest bool `json:"is_latest,omitempty"`
|
||||
}
|
||||
@@ -59,20 +59,7 @@ type ObjectResponse struct {
|
||||
func getObjectsOptionsFromReq(request ObjectsRequest) (*objectsListOpts, error) {
|
||||
pOptions := objectsListOpts{
|
||||
BucketName: request.BucketName,
|
||||
Prefix: "",
|
||||
}
|
||||
|
||||
prefix := request.Prefix
|
||||
|
||||
if prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
LogError("error decoding prefix: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pOptions.Prefix = string(decodedPrefix)
|
||||
Prefix: request.Prefix,
|
||||
}
|
||||
|
||||
if request.Mode == "rewind" {
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -97,11 +97,11 @@ func TestWSRewindObjects(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
mcListMock = func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent {
|
||||
mcListMock = func(_ context.Context, _ mc.ListOptions) <-chan *mc.ClientContent {
|
||||
ch := make(chan *mc.ClientContent)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
@@ -206,11 +206,11 @@ func TestWSListObjects(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
minioListObjectsMock = func(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo {
|
||||
minioListObjectsMock = func(_ context.Context, _ string, _ minio.ListObjectsOptions) <-chan minio.ObjectInfo {
|
||||
ch := make(chan minio.ObjectInfo)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -24,17 +24,16 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
bucketApi "github.com/minio/console/restapi/operations/bucket"
|
||||
policyApi "github.com/minio/console/restapi/operations/policy"
|
||||
bucketApi "github.com/minio/console/api/operations/bucket"
|
||||
policyApi "github.com/minio/console/api/operations/policy"
|
||||
s3 "github.com/minio/minio-go/v7"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
|
||||
policies "github.com/minio/console/restapi/policy"
|
||||
policies "github.com/minio/console/api/policy"
|
||||
)
|
||||
|
||||
func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
@@ -42,7 +41,7 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.PolicyListPoliciesHandler = policyApi.ListPoliciesHandlerFunc(func(params policyApi.ListPoliciesParams, session *models.Principal) middleware.Responder {
|
||||
listPoliciesResponse, err := getListPoliciesResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListPoliciesDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewListPoliciesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewListPoliciesOK().WithPayload(listPoliciesResponse)
|
||||
})
|
||||
@@ -50,7 +49,7 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.PolicyPolicyInfoHandler = policyApi.PolicyInfoHandlerFunc(func(params policyApi.PolicyInfoParams, session *models.Principal) middleware.Responder {
|
||||
policyInfo, err := getPolicyInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewPolicyInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewPolicyInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewPolicyInfoOK().WithPayload(policyInfo)
|
||||
})
|
||||
@@ -58,78 +57,78 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.PolicyAddPolicyHandler = policyApi.AddPolicyHandlerFunc(func(params policyApi.AddPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getAddPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewAddPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewAddPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewAddPolicyCreated().WithPayload(policyResponse)
|
||||
})
|
||||
// Remove Policy
|
||||
api.PolicyRemovePolicyHandler = policyApi.RemovePolicyHandlerFunc(func(params policyApi.RemovePolicyParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRemovePolicyResponse(session, params); err != nil {
|
||||
return policyApi.NewRemovePolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewRemovePolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewRemovePolicyNoContent()
|
||||
})
|
||||
// Set Policy
|
||||
api.PolicySetPolicyHandler = policyApi.SetPolicyHandlerFunc(func(params policyApi.SetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetPolicyResponse(session, params); err != nil {
|
||||
return policyApi.NewSetPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewSetPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewSetPolicyNoContent()
|
||||
})
|
||||
// Set Policy Multiple User/Groups
|
||||
api.PolicySetPolicyMultipleHandler = policyApi.SetPolicyMultipleHandlerFunc(func(params policyApi.SetPolicyMultipleParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetPolicyMultipleResponse(session, params); err != nil {
|
||||
return policyApi.NewSetPolicyMultipleDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewSetPolicyMultipleDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewSetPolicyMultipleNoContent()
|
||||
})
|
||||
api.BucketListPoliciesWithBucketHandler = bucketApi.ListPoliciesWithBucketHandlerFunc(func(params bucketApi.ListPoliciesWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getListPoliciesWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListPoliciesWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewListPoliciesWithBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListPoliciesWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketListAccessRulesWithBucketHandler = bucketApi.ListAccessRulesWithBucketHandlerFunc(func(params bucketApi.ListAccessRulesWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getListAccessRulesWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListAccessRulesWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewListAccessRulesWithBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListAccessRulesWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketSetAccessRuleWithBucketHandler = bucketApi.SetAccessRuleWithBucketHandlerFunc(func(params bucketApi.SetAccessRuleWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getSetAccessRuleWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewSetAccessRuleWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewSetAccessRuleWithBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewSetAccessRuleWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketDeleteAccessRuleWithBucketHandler = bucketApi.DeleteAccessRuleWithBucketHandlerFunc(func(params bucketApi.DeleteAccessRuleWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getDeleteAccessRuleWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteAccessRuleWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewDeleteAccessRuleWithBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewDeleteAccessRuleWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.PolicyListUsersForPolicyHandler = policyApi.ListUsersForPolicyHandlerFunc(func(params policyApi.ListUsersForPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyUsersResponse, err := getListUsersForPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListUsersForPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewListUsersForPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewListUsersForPolicyOK().WithPayload(policyUsersResponse)
|
||||
})
|
||||
api.PolicyListGroupsForPolicyHandler = policyApi.ListGroupsForPolicyHandlerFunc(func(params policyApi.ListGroupsForPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyGroupsResponse, err := getListGroupsForPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListGroupsForPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewListGroupsForPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewListGroupsForPolicyOK().WithPayload(policyGroupsResponse)
|
||||
})
|
||||
// Gets policies for currently logged in user
|
||||
api.PolicyGetUserPolicyHandler = policyApi.GetUserPolicyHandlerFunc(func(params policyApi.GetUserPolicyParams, session *models.Principal) middleware.Responder {
|
||||
userPolicyResponse, err := getUserPolicyResponse(session)
|
||||
userPolicyResponse, err := getUserPolicyResponse(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return policyApi.NewGetUserPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewGetUserPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewGetUserPolicyOK().WithPayload(userPolicyResponse)
|
||||
})
|
||||
@@ -137,17 +136,17 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.PolicyGetSAUserPolicyHandler = policyApi.GetSAUserPolicyHandlerFunc(func(params policyApi.GetSAUserPolicyParams, session *models.Principal) middleware.Responder {
|
||||
userPolicyResponse, err := getSAUserPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewGetSAUserPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
return policyApi.NewGetSAUserPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewGetSAUserPolicyOK().WithPayload(userPolicyResponse)
|
||||
})
|
||||
}
|
||||
|
||||
func getListAccessRulesWithBucketResponse(session *models.Principal, params bucketApi.ListAccessRulesWithBucketParams) (*models.ListAccessRulesResponse, *models.Error) {
|
||||
func getListAccessRulesWithBucketResponse(session *models.Principal, params bucketApi.ListAccessRulesWithBucketParams) (*models.ListAccessRulesResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucket := params.Bucket
|
||||
client, err := newS3BucketClient(session, bucket, "")
|
||||
client, err := newS3BucketClient(session, bucket, "", getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -159,11 +158,11 @@ func getListAccessRulesWithBucketResponse(session *models.Principal, params buck
|
||||
return &models.ListAccessRulesResponse{AccessRules: accessRuleList}, nil
|
||||
}
|
||||
|
||||
func getSetAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.SetAccessRuleWithBucketParams) (bool, *models.Error) {
|
||||
func getSetAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.SetAccessRuleWithBucketParams) (bool, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
prefixAccess := params.Prefixaccess
|
||||
client, err := newS3BucketClient(session, params.Bucket, prefixAccess.Prefix)
|
||||
client, err := newS3BucketClient(session, params.Bucket, prefixAccess.Prefix, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -179,12 +178,12 @@ func getSetAccessRuleWithBucketResponse(session *models.Principal, params bucket
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getDeleteAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.DeleteAccessRuleWithBucketParams) (bool, *models.Error) {
|
||||
func getDeleteAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.DeleteAccessRuleWithBucketParams) (bool, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucket := params.Bucket
|
||||
prefix := params.Prefix
|
||||
client, err := newS3BucketClient(session, bucket, prefix.Prefix)
|
||||
client, err := newS3BucketClient(session, bucket, prefix.Prefix, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -195,10 +194,10 @@ func getDeleteAccessRuleWithBucketResponse(session *models.Principal, params buc
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getListPoliciesWithBucketResponse(session *models.Principal, params bucketApi.ListPoliciesWithBucketParams) (*models.ListPoliciesResponse, *models.Error) {
|
||||
func getListPoliciesWithBucketResponse(session *models.Principal, params bucketApi.ListPoliciesWithBucketParams) (*models.ListPoliciesResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -281,10 +280,10 @@ func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, err
|
||||
}
|
||||
|
||||
// getListPoliciesResponse performs listPolicies() and serializes it to the handler's output
|
||||
func getListPoliciesResponse(session *models.Principal, params policyApi.ListPoliciesParams) (*models.ListPoliciesResponse, *models.Error) {
|
||||
func getListPoliciesResponse(session *models.Principal, params policyApi.ListPoliciesParams) (*models.ListPoliciesResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -305,14 +304,10 @@ func getListPoliciesResponse(session *models.Principal, params policyApi.ListPol
|
||||
}
|
||||
|
||||
// getListUsersForPoliciesResponse performs lists users affected by a given policy.
|
||||
func getListUsersForPolicyResponse(session *models.Principal, params policyApi.ListUsersForPolicyParams) ([]string, *models.Error) {
|
||||
func getListUsersForPolicyResponse(session *models.Principal, params policyApi.ListUsersForPolicyParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
policy, err := utils.DecodeBase64(params.Policy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -325,12 +320,12 @@ func getListUsersForPolicyResponse(session *models.Principal, params policyApi.L
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
if policies[i].Name == policy {
|
||||
if policies[i].Name == params.Policy {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", params.Policy))
|
||||
}
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
@@ -340,7 +335,7 @@ func getListUsersForPolicyResponse(session *models.Principal, params policyApi.L
|
||||
var filteredUsers []string
|
||||
for _, user := range users {
|
||||
for _, upolicy := range user.Policy {
|
||||
if upolicy == policy {
|
||||
if upolicy == params.Policy {
|
||||
filteredUsers = append(filteredUsers, user.AccessKey)
|
||||
break
|
||||
}
|
||||
@@ -350,8 +345,8 @@ func getListUsersForPolicyResponse(session *models.Principal, params policyApi.L
|
||||
return filteredUsers, nil
|
||||
}
|
||||
|
||||
func getUserPolicyResponse(session *models.Principal) (string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getUserPolicyResponse(ctx context.Context, session *models.Principal) (string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
@@ -360,7 +355,7 @@ func getUserPolicyResponse(session *models.Principal) (string, *models.Error) {
|
||||
tokenClaims, _ := getClaimsFromToken(session.STSSessionToken)
|
||||
|
||||
// initialize admin client
|
||||
mAdminClient, err := NewMinioAdminClient(&models.Principal{
|
||||
mAdminClient, err := NewMinioAdminClient(ctx, &models.Principal{
|
||||
STSAccessKeyID: session.STSAccessKeyID,
|
||||
STSSecretAccessKey: session.STSSecretAccessKey,
|
||||
STSSessionToken: session.STSSessionToken,
|
||||
@@ -379,7 +374,7 @@ func getUserPolicyResponse(session *models.Principal) (string, *models.Error) {
|
||||
return string(rawPolicy), nil
|
||||
}
|
||||
|
||||
func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUserPolicyParams) (*models.AUserPolicyResponse, *models.Error) {
|
||||
func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUserPolicyParams) (*models.AUserPolicyResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
@@ -387,7 +382,7 @@ func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUs
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound)
|
||||
}
|
||||
// initialize admin client
|
||||
mAdminClient, err := NewMinioAdminClient(&models.Principal{
|
||||
mAdminClient, err := NewMinioAdminClient(params.HTTPRequest.Context(), &models.Principal{
|
||||
STSAccessKeyID: session.STSAccessKeyID,
|
||||
STSSecretAccessKey: session.STSSecretAccessKey,
|
||||
STSSessionToken: session.STSSessionToken,
|
||||
@@ -397,12 +392,7 @@ func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUs
|
||||
}
|
||||
userAdminClient := AdminClient{Client: mAdminClient}
|
||||
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
user, err := getUserInfo(ctx, userAdminClient, userName)
|
||||
user, err := getUserInfo(ctx, userAdminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -458,19 +448,15 @@ func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUs
|
||||
return getUserPoliciesResponse, nil
|
||||
}
|
||||
|
||||
func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.ListGroupsForPolicyParams) ([]string, *models.Error) {
|
||||
func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.ListGroupsForPolicyParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
policy, err := utils.DecodeBase64(params.Policy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
@@ -478,12 +464,12 @@ func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
if policies[i].Name == policy {
|
||||
if policies[i].Name == params.Policy {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", params.Policy))
|
||||
}
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
@@ -499,7 +485,7 @@ func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.
|
||||
}
|
||||
groupPolicies := strings.Split(info.Policy, ",")
|
||||
for _, groupPolicy := range groupPolicies {
|
||||
if groupPolicy == policy {
|
||||
if groupPolicy == params.Policy {
|
||||
filteredGroups = append(filteredGroups, group)
|
||||
}
|
||||
}
|
||||
@@ -518,17 +504,13 @@ func removePolicy(ctx context.Context, client MinioAdmin, name string) error {
|
||||
}
|
||||
|
||||
// getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output
|
||||
func getRemovePolicyResponse(session *models.Principal, params policyApi.RemovePolicyParams) *models.Error {
|
||||
func getRemovePolicyResponse(session *models.Principal, params policyApi.RemovePolicyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return ErrorWithContext(ctx, ErrPolicyNameNotInRequest)
|
||||
}
|
||||
policyName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -536,7 +518,7 @@ func getRemovePolicyResponse(session *models.Principal, params policyApi.RemoveP
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := removePolicy(ctx, adminClient, policyName); err != nil {
|
||||
if err := removePolicy(ctx, adminClient, params.Name); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
@@ -562,16 +544,13 @@ func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*mo
|
||||
}
|
||||
|
||||
// getAddPolicyResponse performs addPolicy() and serializes it to the handler's output
|
||||
func getAddPolicyResponse(session *models.Principal, params policyApi.AddPolicyParams) (*models.Policy, *models.Error) {
|
||||
func getAddPolicyResponse(session *models.Principal, params policyApi.AddPolicyParams) (*models.Policy, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Body == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyBodyNotInRequest)
|
||||
}
|
||||
if strings.Contains(*params.Body.Name, " ") {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNameContainsSpace)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -613,21 +592,17 @@ func getPolicyStatements(ctx context.Context, client MinioAdmin, name string) ([
|
||||
}
|
||||
|
||||
// getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output
|
||||
func getPolicyInfoResponse(session *models.Principal, params policyApi.PolicyInfoParams) (*models.Policy, *models.Error) {
|
||||
func getPolicyInfoResponse(session *models.Principal, params policyApi.PolicyInfoParams) (*models.Policy, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policyName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
policy, err := policyInfo(ctx, adminClient, policyName)
|
||||
policy, err := policyInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -644,11 +619,11 @@ func SetPolicy(ctx context.Context, client MinioAdmin, name, entityName string,
|
||||
}
|
||||
|
||||
// getSetPolicyResponse() performs SetPolicy() and serializes it to the handler's output
|
||||
func getSetPolicyResponse(session *models.Principal, params policyApi.SetPolicyParams) *models.Error {
|
||||
func getSetPolicyResponse(session *models.Principal, params policyApi.SetPolicyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// Removing this section
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -662,10 +637,10 @@ func getSetPolicyResponse(session *models.Principal, params policyApi.SetPolicyP
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSetPolicyMultipleResponse(session *models.Principal, params policyApi.SetPolicyMultipleParams) *models.Error {
|
||||
func getSetPolicyMultipleResponse(session *models.Principal, params policyApi.SetPolicyMultipleParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -26,50 +26,15 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioListPoliciesMock func() (map[string]*iampolicy.Policy, error)
|
||||
|
||||
var (
|
||||
minioGetPolicyMock func(name string) (*iampolicy.Policy, error)
|
||||
minioRemovePolicyMock func(name string) error
|
||||
minioAddPolicyMock func(name string, policy *iampolicy.Policy) error
|
||||
minioSetPolicyMock func(policyName, entityName string, isGroup bool) error
|
||||
)
|
||||
|
||||
// mock function of listPolicies()
|
||||
func (ac adminClientMock) listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error) {
|
||||
return minioListPoliciesMock()
|
||||
}
|
||||
|
||||
// mock function of getPolicy()
|
||||
func (ac adminClientMock) getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error) {
|
||||
return minioGetPolicyMock(name)
|
||||
}
|
||||
|
||||
// mock function of removePolicy()
|
||||
func (ac adminClientMock) removePolicy(ctx context.Context, name string) error {
|
||||
return minioRemovePolicyMock(name)
|
||||
}
|
||||
|
||||
// mock function of addPolicy()
|
||||
func (ac adminClientMock) addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error {
|
||||
return minioAddPolicyMock(name, policy)
|
||||
}
|
||||
|
||||
// mock function SetPolicy()
|
||||
func (ac adminClientMock) setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error {
|
||||
return minioSetPolicyMock(policyName, entityName, isGroup)
|
||||
}
|
||||
|
||||
func TestListPolicies(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
// mock function response from listPolicies()
|
||||
minioListPoliciesMock = func() (map[string]*iampolicy.Policy, error) {
|
||||
var readonly iampolicy.Policy
|
||||
@@ -115,10 +80,10 @@ func TestRemovePolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
// Test-1 : removePolicy() remove an existing policy
|
||||
policyToRemove := "console-policy"
|
||||
minioRemovePolicyMock = func(name string) error {
|
||||
minioRemovePolicyMock = func(_ string) error {
|
||||
return nil
|
||||
}
|
||||
function := "removePolicy()"
|
||||
@@ -126,7 +91,7 @@ func TestRemovePolicy(t *testing.T) {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-2 : removePolicy() Return error and see that the error is handled correctly and returned
|
||||
minioRemovePolicyMock = func(name string) error {
|
||||
minioRemovePolicyMock = func(_ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := removePolicy(ctx, adminClient, policyToRemove); funcAssert.Error(err) {
|
||||
@@ -138,13 +103,13 @@ func TestAddPolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
policyName := "new-policy"
|
||||
policyDefinition := "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}"
|
||||
minioAddPolicyMock = func(name string, policy *iampolicy.Policy) error {
|
||||
minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
|
||||
return nil
|
||||
}
|
||||
minioGetPolicyMock = func(name string) (*iampolicy.Policy, error) {
|
||||
minioGetPolicyMock = func(_ string) (*iampolicy.Policy, error) {
|
||||
policy := "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}"
|
||||
iamp, err := iampolicy.ParseConfig(bytes.NewReader([]byte(policy)))
|
||||
if err != nil {
|
||||
@@ -173,17 +138,17 @@ func TestAddPolicy(t *testing.T) {
|
||||
funcAssert.Equal(expectedPolicy, actualPolicy)
|
||||
}
|
||||
// Test-2 : addPolicy() got an error while adding policy
|
||||
minioAddPolicyMock = func(name string, policy *iampolicy.Policy) error {
|
||||
minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
// Test-3 : addPolicy() got an error while retrieving policy
|
||||
minioAddPolicyMock = func(name string, policy *iampolicy.Policy) error {
|
||||
minioAddPolicyMock = func(_ string, _ *iampolicy.Policy) error {
|
||||
return nil
|
||||
}
|
||||
minioGetPolicyMock = func(name string) (*iampolicy.Policy, error) {
|
||||
minioGetPolicyMock = func(_ string) (*iampolicy.Policy, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); funcAssert.Error(err) {
|
||||
@@ -195,11 +160,11 @@ func TestSetPolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
policyName := "readOnly"
|
||||
entityName := "alevsk"
|
||||
entityObject := models.PolicyEntityUser
|
||||
minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error {
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
}
|
||||
// Test-1 : SetPolicy() set policy to user
|
||||
@@ -216,7 +181,7 @@ func TestSetPolicy(t *testing.T) {
|
||||
}
|
||||
// Test-3 : SetPolicy() set policy to user and get error
|
||||
entityObject = models.PolicyEntityUser
|
||||
minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error {
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
|
||||
@@ -224,7 +189,7 @@ func TestSetPolicy(t *testing.T) {
|
||||
}
|
||||
// Test-4 : SetPolicy() set policy to group and get error
|
||||
entityObject = models.PolicyEntityGroup
|
||||
minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error {
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
|
||||
@@ -235,7 +200,7 @@ func TestSetPolicy(t *testing.T) {
|
||||
func Test_SetPolicyMultiple(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
policyName string
|
||||
@@ -254,7 +219,7 @@ func Test_SetPolicyMultiple(t *testing.T) {
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{"user1", "user2"},
|
||||
groups: []models.IamEntity{"group1", "group2"},
|
||||
setPolicyFunc: func(policyName, entityName string, isGroup bool) error {
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -266,7 +231,7 @@ func Test_SetPolicyMultiple(t *testing.T) {
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{"user1", "user2"},
|
||||
groups: []models.IamEntity{"group1", "group2"},
|
||||
setPolicyFunc: func(policyName, entityName string, isGroup bool) error {
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return errors.New("error set")
|
||||
},
|
||||
},
|
||||
@@ -279,7 +244,7 @@ func Test_SetPolicyMultiple(t *testing.T) {
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{},
|
||||
groups: []models.IamEntity{},
|
||||
setPolicyFunc: func(policyName, entityName string, isGroup bool) error {
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
@@ -287,7 +252,7 @@ func Test_SetPolicyMultiple(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
minioSetPolicyMock = tt.args.setPolicyFunc
|
||||
got := setPolicyMultipleEntities(ctx, adminClient, tt.args.policyName, tt.args.users, tt.args.groups)
|
||||
if !reflect.DeepEqual(got, tt.errorExpected) {
|
||||
@@ -408,7 +373,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
if got := policyMatchesBucket(tt.args.ctx, tt.args.policy, tt.args.bucket); got != tt.want {
|
||||
t.Errorf("policyMatchesBucket() = %v, want %v", got, tt.want)
|
||||
}
|
||||
51
api/admin_profiling.go
Normal file
51
api/admin_profiling.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
type profileOptions struct {
|
||||
Types string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
func getProfileOptionsFromReq(req *http.Request) (*profileOptions, error) {
|
||||
pOptions := profileOptions{}
|
||||
pOptions.Types = req.FormValue("types")
|
||||
pOptions.Duration = 10 * time.Second // TODO: make this configurable
|
||||
return &pOptions, nil
|
||||
}
|
||||
|
||||
func startProfiling(ctx context.Context, conn WSConn, client MinioAdmin, pOpts *profileOptions) error {
|
||||
data, err := client.startProfiling(ctx, madmin.ProfilerType(pOpts.Types), pOpts.Duration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message, err := io.ReadAll(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.writeMessage(websocket.BinaryMessage, message)
|
||||
}
|
||||
91
api/admin_profiling_test.go
Normal file
91
api/admin_profiling_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Implementing fake closingBuffer to mock stopProfiling() (io.ReadCloser, error)
|
||||
type ClosingBuffer struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
// Implementing a fake Close function for io.ReadCloser
|
||||
func (cb *ClosingBuffer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestStartProfiling(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startProfiling()"
|
||||
testOptions := &profileOptions{
|
||||
Types: "cpu",
|
||||
}
|
||||
|
||||
// Test-1 : startProfiling() Get response from MinIO server with one profiling object without errors
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(_ madmin.ProfilerType, _ time.Duration) (io.ReadCloser, error) {
|
||||
return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil
|
||||
}
|
||||
// mock function response from mockConn.writeMessage()
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
err := startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(err, nil)
|
||||
|
||||
// Test-2 : startProfiling() Correctly handles errors returned by MinIO
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(_ madmin.ProfilerType, _ time.Duration) (io.ReadCloser, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
err = startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: getProfileOptionsFromReq() correctly returns profile options from request
|
||||
u, _ := url.Parse("ws://localhost/ws/profile?types=cpu,mem,block,mutex,trace,threads,goroutines")
|
||||
req := &http.Request{
|
||||
URL: u,
|
||||
}
|
||||
opts, err := getProfileOptionsFromReq(req)
|
||||
if assert.NoError(err) {
|
||||
expectedOptions := profileOptions{
|
||||
Types: "cpu,mem,block,mutex,trace,threads,goroutines",
|
||||
}
|
||||
assert.Equal(expectedOptions.Types, opts.Types)
|
||||
}
|
||||
}
|
||||
116
api/admin_releases.go
Normal file
116
api/admin_releases.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
release "github.com/minio/console/api/operations/release"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
)
|
||||
|
||||
var (
|
||||
releaseServiceHostEnvVar = "RELEASE_SERVICE_HOST"
|
||||
defaultReleaseServiceHost = "https://enterprise-updates.ic.min.dev"
|
||||
)
|
||||
|
||||
func registerReleasesHandlers(api *operations.ConsoleAPI) {
|
||||
api.ReleaseListReleasesHandler = release.ListReleasesHandlerFunc(func(params release.ListReleasesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetReleaseListResponse(session, params)
|
||||
if err != nil {
|
||||
return release.NewListReleasesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return release.NewListReleasesOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetReleaseListResponse(_ *models.Principal, params release.ListReleasesParams) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
repo := params.Repo
|
||||
currentRelease := ""
|
||||
if params.Current != nil {
|
||||
currentRelease = *params.Current
|
||||
}
|
||||
search := ""
|
||||
if params.Search != nil {
|
||||
search = *params.Search
|
||||
}
|
||||
filter := ""
|
||||
if params.Filter != nil {
|
||||
filter = *params.Filter
|
||||
}
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest))
|
||||
return releaseList(ctx, repo, currentRelease, search, filter)
|
||||
}
|
||||
|
||||
func releaseList(ctx context.Context, repo, currentRelease, search, filter string) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
serviceURL := getReleaseServiceURL()
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
releases, err := getReleases(serviceURL, repo, currentRelease, search, filter, clientIP)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func getReleaseServiceURL() string {
|
||||
host := env.Get(releaseServiceHostEnvVar, defaultReleaseServiceHost)
|
||||
return fmt.Sprintf("%s/releases", host)
|
||||
}
|
||||
|
||||
func getReleases(endpoint, repo, currentRelease, search, filter, clientIP string) (*models.ReleaseListResponse, error) {
|
||||
rl := &models.ReleaseListResponse{}
|
||||
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := &url.Values{}
|
||||
q.Add("repo", repo)
|
||||
q.Add("search", search)
|
||||
q.Add("filter", filter)
|
||||
q.Add("current", currentRelease)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := GetConsoleHTTPClient(clientIP)
|
||||
client.Timeout = time.Second * 5
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("error getting releases: %s", resp.Status)
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&rl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rl, nil
|
||||
}
|
||||
104
api/admin_releases_test.go
Normal file
104
api/admin_releases_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
release "github.com/minio/console/api/operations/release"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ReleasesTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
getServer *httptest.Server
|
||||
withError bool
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.getServer = httptest.NewServer(http.HandlerFunc(suite.getHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(releaseServiceHostEnvVar)
|
||||
os.Setenv(releaseServiceHostEnvVar, suite.getServer.URL)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TearDownSuite() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(releaseServiceHostEnvVar, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(releaseServiceHostEnvVar)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) getHandler(
|
||||
w http.ResponseWriter, _ *http.Request,
|
||||
) {
|
||||
if suite.withError {
|
||||
w.WriteHeader(400)
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
response := &models.ReleaseListResponse{}
|
||||
bytes, _ := json.Marshal(response)
|
||||
fmt.Fprint(w, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestRegisterReleasesHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assert.Nil(api.ReleaseListReleasesHandler)
|
||||
registerReleasesHandlers(api)
|
||||
suite.assert.NotNil(api.ReleaseListReleasesHandler)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestGetReleasesWithError() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
current := "mock"
|
||||
registerReleasesHandlers(api)
|
||||
params := release.NewListReleasesParams()
|
||||
params.Current = ¤t
|
||||
params.HTTPRequest = &http.Request{}
|
||||
suite.withError = true
|
||||
response := api.ReleaseListReleasesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*release.ListReleasesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestGetReleasesWithoutError() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
registerReleasesHandlers(api)
|
||||
params := release.NewListReleasesParams()
|
||||
params.HTTPRequest = &http.Request{}
|
||||
suite.withError = false
|
||||
response := api.ReleaseListReleasesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*release.ListReleasesOK)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func TestReleases(t *testing.T) {
|
||||
suite.Run(t, new(ReleasesTestSuite))
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -24,13 +24,15 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
bucketApi "github.com/minio/console/api/operations/bucket"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
bucketApi "github.com/minio/console/restapi/operations/bucket"
|
||||
"github.com/minio/minio-go/v7/pkg/replication"
|
||||
)
|
||||
|
||||
@@ -45,7 +47,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketListRemoteBucketsHandler = bucketApi.ListRemoteBucketsHandlerFunc(func(params bucketApi.ListRemoteBucketsParams, session *models.Principal) middleware.Responder {
|
||||
listResp, err := getListRemoteBucketsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListRemoteBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewListRemoteBucketsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListRemoteBucketsOK().WithPayload(listResp)
|
||||
})
|
||||
@@ -54,7 +56,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketRemoteBucketDetailsHandler = bucketApi.RemoteBucketDetailsHandlerFunc(func(params bucketApi.RemoteBucketDetailsParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getRemoteBucketDetailsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewRemoteBucketDetailsDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewRemoteBucketDetailsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewRemoteBucketDetailsOK().WithPayload(response)
|
||||
})
|
||||
@@ -63,7 +65,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketDeleteRemoteBucketHandler = bucketApi.DeleteRemoteBucketHandlerFunc(func(params bucketApi.DeleteRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getDeleteRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteRemoteBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewDeleteRemoteBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewDeleteRemoteBucketNoContent()
|
||||
})
|
||||
@@ -72,7 +74,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketAddRemoteBucketHandler = bucketApi.AddRemoteBucketHandlerFunc(func(params bucketApi.AddRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewAddRemoteBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewAddRemoteBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewAddRemoteBucketCreated()
|
||||
})
|
||||
@@ -81,17 +83,17 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketSetMultiBucketReplicationHandler = bucketApi.SetMultiBucketReplicationHandlerFunc(func(params bucketApi.SetMultiBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := setMultiBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewSetMultiBucketReplicationDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewSetMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewSetMultiBucketReplicationOK().WithPayload(response)
|
||||
})
|
||||
|
||||
// list external buckets
|
||||
api.BucketListExternalBucketsHandler = bucketApi.ListExternalBucketsHandlerFunc(func(params bucketApi.ListExternalBucketsParams, session *models.Principal) middleware.Responder {
|
||||
api.BucketListExternalBucketsHandler = bucketApi.ListExternalBucketsHandlerFunc(func(params bucketApi.ListExternalBucketsParams, _ *models.Principal) middleware.Responder {
|
||||
response, err := listExternalBucketsResponse(params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListExternalBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewListExternalBucketsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewListExternalBucketsOK().WithPayload(response)
|
||||
@@ -101,7 +103,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketDeleteBucketReplicationRuleHandler = bucketApi.DeleteBucketReplicationRuleHandlerFunc(func(params bucketApi.DeleteBucketReplicationRuleParams, session *models.Principal) middleware.Responder {
|
||||
err := deleteReplicationRuleResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteBucketReplicationRuleDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewDeleteBucketReplicationRuleDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteBucketReplicationRuleNoContent()
|
||||
@@ -111,13 +113,13 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketDeleteAllReplicationRulesHandler = bucketApi.DeleteAllReplicationRulesHandlerFunc(func(params bucketApi.DeleteAllReplicationRulesParams, session *models.Principal) middleware.Responder {
|
||||
err := deleteBucketReplicationRulesResponse(session, params)
|
||||
if err != nil {
|
||||
if err.Code == 500 && *err.DetailedMessage == "The remote target does not exist" {
|
||||
if err.Code == 500 && err.APIError.DetailedMessage == "The remote target does not exist" {
|
||||
// We should ignore this MinIO error when deleting all replication rules
|
||||
return bucketApi.NewDeleteAllReplicationRulesNoContent() // This will return 204 as per swagger spec
|
||||
}
|
||||
// If there is a different error, then we should handle it
|
||||
// This will return a generic error with err.Code (likely a 500 or 404) and its *err.DetailedMessage
|
||||
return bucketApi.NewDeleteAllReplicationRulesDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewDeleteAllReplicationRulesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewDeleteAllReplicationRulesNoContent()
|
||||
})
|
||||
@@ -126,7 +128,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketDeleteSelectedReplicationRulesHandler = bucketApi.DeleteSelectedReplicationRulesHandlerFunc(func(params bucketApi.DeleteSelectedReplicationRulesParams, session *models.Principal) middleware.Responder {
|
||||
err := deleteSelectedReplicationRulesResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesNoContent()
|
||||
@@ -136,49 +138,38 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketUpdateMultiBucketReplicationHandler = bucketApi.UpdateMultiBucketReplicationHandlerFunc(func(params bucketApi.UpdateMultiBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
err := updateBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewUpdateMultiBucketReplicationDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewUpdateMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewUpdateMultiBucketReplicationCreated()
|
||||
})
|
||||
}
|
||||
|
||||
func getListRemoteBucketsResponse(session *models.Principal, params bucketApi.ListRemoteBucketsParams) (*models.ListRemoteBucketsResponse, *models.Error) {
|
||||
func getListRemoteBucketsResponse(session *models.Principal, params bucketApi.ListRemoteBucketsParams) (*models.ListRemoteBucketsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
buckets, err := listRemoteBuckets(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error listing remote buckets: %v", err))
|
||||
}
|
||||
return &models.ListRemoteBucketsResponse{
|
||||
Buckets: buckets,
|
||||
Total: int64(len(buckets)),
|
||||
}, nil
|
||||
return listRemoteBuckets(ctx, adminClient)
|
||||
}
|
||||
|
||||
func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, *models.Error) {
|
||||
func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
bucket, err := getRemoteBucket(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error getting remote bucket details: %v", err))
|
||||
}
|
||||
return bucket, nil
|
||||
return getRemoteBucket(ctx, adminClient, params.Name)
|
||||
}
|
||||
|
||||
func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) *models.Error {
|
||||
func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
@@ -190,10 +181,10 @@ func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.D
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) *models.Error {
|
||||
func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
@@ -205,11 +196,11 @@ func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddR
|
||||
return nil
|
||||
}
|
||||
|
||||
func listRemoteBuckets(ctx context.Context, client MinioAdmin) ([]*models.RemoteBucket, error) {
|
||||
func listRemoteBuckets(ctx context.Context, client MinioAdmin) (*models.ListRemoteBucketsResponse, *CodedAPIError) {
|
||||
var remoteBuckets []*models.RemoteBucket
|
||||
buckets, err := client.listRemoteBuckets(ctx, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error listing remote buckets: %v", err))
|
||||
}
|
||||
for _, bucket := range buckets {
|
||||
remoteBucket := &models.RemoteBucket{
|
||||
@@ -230,16 +221,20 @@ func listRemoteBuckets(ctx context.Context, client MinioAdmin) ([]*models.Remote
|
||||
}
|
||||
remoteBuckets = append(remoteBuckets, remoteBucket)
|
||||
}
|
||||
return remoteBuckets, nil
|
||||
|
||||
return &models.ListRemoteBucketsResponse{
|
||||
Buckets: remoteBuckets,
|
||||
Total: int64(len(remoteBuckets)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getRemoteBucket(ctx context.Context, client MinioAdmin, name string) (*models.RemoteBucket, error) {
|
||||
func getRemoteBucket(ctx context.Context, client MinioAdmin, name string) (*models.RemoteBucket, *CodedAPIError) {
|
||||
remoteBucket, err := client.getRemoteBucket(ctx, name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error getting remote bucket details: %v", err))
|
||||
}
|
||||
if remoteBucket == nil {
|
||||
return nil, errors.New("bucket not found")
|
||||
return nil, ErrorWithContext(ctx, "error getting remote bucket details: bucket not found")
|
||||
}
|
||||
return &models.RemoteBucket{
|
||||
AccessKey: &remoteBucket.Credentials.AccessKey,
|
||||
@@ -297,7 +292,7 @@ func addRemoteBucket(ctx context.Context, client MinioAdmin, params models.Creat
|
||||
return bucketARN, err
|
||||
}
|
||||
|
||||
func addBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, bucketName, prefix, destinationARN string, repDelMark, repDels, repMeta bool, tags string, priority int32, storageClass string) error {
|
||||
func addBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, bucketName, prefix, destinationARN string, repExistingObj, repDelMark, repDels, repMeta bool, tags string, priority int32, storageClass string) error {
|
||||
// we will tolerate this call failing
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
@@ -317,8 +312,8 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi
|
||||
} else { // User picked priority, we try to set this manually
|
||||
maxPrio = int(priority)
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix)
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix, clientIP)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error creating S3Client: %v", err))
|
||||
return err
|
||||
@@ -342,13 +337,18 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi
|
||||
repMetaStatus = "enable"
|
||||
}
|
||||
|
||||
existingRepStatus := "disable"
|
||||
if repExistingObj {
|
||||
existingRepStatus = "enable"
|
||||
}
|
||||
|
||||
opts := replication.Options{
|
||||
Priority: fmt.Sprintf("%d", maxPrio),
|
||||
RuleStatus: "enable",
|
||||
DestBucket: destinationARN,
|
||||
Op: replication.AddOption,
|
||||
TagString: tags,
|
||||
ExistingObjectReplicate: "enable", // enabled by default
|
||||
ExistingObjectReplicate: existingRepStatus,
|
||||
ReplicateDeleteMarkers: repDelMarkStatus,
|
||||
ReplicateDeletes: repDelsStatus,
|
||||
ReplicaSync: repMetaStatus,
|
||||
@@ -372,7 +372,8 @@ func editBucketReplicationItem(ctx context.Context, session *models.Principal, m
|
||||
|
||||
maxPrio := int(priority)
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix)
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
@@ -463,6 +464,7 @@ func setMultiBucketReplication(ctx context.Context, session *models.Principal, c
|
||||
sourceBucket,
|
||||
params.Body.Prefix,
|
||||
arn,
|
||||
params.Body.ReplicateExistingObjects,
|
||||
params.Body.ReplicateDeleteMarkers,
|
||||
params.Body.ReplicateDeletes,
|
||||
params.Body.ReplicateMetadata,
|
||||
@@ -506,17 +508,17 @@ func setMultiBucketReplication(ctx context.Context, session *models.Principal, c
|
||||
return resultsList
|
||||
}
|
||||
|
||||
func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *models.Error) {
|
||||
func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err))
|
||||
}
|
||||
@@ -549,27 +551,26 @@ func setMultiBucketReplicationResponse(session *models.Principal, params bucketA
|
||||
return &resultsParsed, nil
|
||||
}
|
||||
|
||||
func listExternalBucketsResponse(params bucketApi.ListExternalBucketsParams) (*models.ListBucketsResponse, *models.Error) {
|
||||
func listExternalBucketsResponse(params bucketApi.ListExternalBucketsParams) (*models.ListBucketsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
remoteAdmin, err := newAdminFromCreds(*params.Body.AccessKey, *params.Body.SecretKey, *params.Body.TargetURL, *params.Body.UseTLS)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
remoteClient := AdminClient{Client: remoteAdmin}
|
||||
buckets, err := getAccountBuckets(ctx, remoteClient)
|
||||
return listExternalBuckets(ctx, AdminClient{Client: remoteAdmin})
|
||||
}
|
||||
|
||||
func listExternalBuckets(ctx context.Context, client MinioAdmin) (*models.ListBucketsResponse, *CodedAPIError) {
|
||||
buckets, err := getAccountBuckets(ctx, client)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
listBucketsResponse := &models.ListBucketsResponse{
|
||||
return &models.ListBucketsResponse{
|
||||
Buckets: buckets,
|
||||
Total: int64(len(buckets)),
|
||||
}
|
||||
return listBucketsResponse, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getARNFromID(conf *replication.Config, rule string) string {
|
||||
@@ -596,7 +597,8 @@ func getARNsFromIDs(conf *replication.Config, rules []string) []string {
|
||||
}
|
||||
|
||||
func deleteReplicationRule(ctx context.Context, session *models.Principal, bucketName, ruleID string) error {
|
||||
mClient, err := newMinioClient(session)
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
mClient, err := newMinioClient(session, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
@@ -609,12 +611,11 @@ func deleteReplicationRule(ctx context.Context, session *models.Principal, bucke
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "", clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
@@ -644,15 +645,16 @@ func deleteReplicationRule(ctx context.Context, session *models.Principal, bucke
|
||||
}
|
||||
|
||||
func deleteAllReplicationRules(ctx context.Context, session *models.Principal, bucketName string) error {
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "", clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
mClient, err := newMinioClient(session, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
@@ -665,7 +667,7 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
@@ -674,7 +676,7 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
err2 := mcClient.deleteAllReplicationRules(ctx)
|
||||
|
||||
if err2 != nil {
|
||||
return err
|
||||
return err2.ToGoError()
|
||||
}
|
||||
|
||||
for i := range cfg.Rules {
|
||||
@@ -688,7 +690,8 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
}
|
||||
|
||||
func deleteSelectedReplicationRules(ctx context.Context, session *models.Principal, bucketName string, rules []string) error {
|
||||
mClient, err := newMinioClient(session)
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
mClient, err := newMinioClient(session, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
@@ -701,7 +704,7 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "", clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
@@ -709,7 +712,7 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
@@ -736,10 +739,10 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.DeleteBucketReplicationRuleParams) *models.Error {
|
||||
func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.DeleteBucketReplicationRuleParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest))
|
||||
err := deleteReplicationRule(ctx, session, params.BucketName, params.RuleID)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
@@ -747,10 +750,10 @@ func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.D
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteBucketReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteAllReplicationRulesParams) *models.Error {
|
||||
func deleteBucketReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteAllReplicationRulesParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest))
|
||||
err := deleteAllReplicationRules(ctx, session, params.BucketName)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
@@ -758,10 +761,12 @@ func deleteBucketReplicationRulesResponse(session *models.Principal, params buck
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSelectedReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteSelectedReplicationRulesParams) *models.Error {
|
||||
func deleteSelectedReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteSelectedReplicationRulesParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest))
|
||||
|
||||
err := deleteSelectedReplicationRules(ctx, session, params.BucketName, params.Rules.Rules)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
@@ -769,11 +774,11 @@ func deleteSelectedReplicationRulesResponse(session *models.Principal, params bu
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *models.Error {
|
||||
func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -797,7 +802,6 @@ func updateBucketReplicationResponse(session *models.Principal, params bucketApi
|
||||
params.Body.Tags,
|
||||
params.Body.Priority,
|
||||
params.Body.StorageClass)
|
||||
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
386
api/admin_remote_buckets_test.go
Normal file
386
api/admin_remote_buckets_test.go
Normal file
@@ -0,0 +1,386 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
bucketApi "github.com/minio/console/api/operations/bucket"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type RemoteBucketsTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
minioClient minioClientMock
|
||||
mockRemoteBucket *models.RemoteBucket
|
||||
mockBucketTarget *madmin.BucketTarget
|
||||
mockListBuckets *models.ListBucketsResponse
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
suite.minioClient = minioClientMock{}
|
||||
suite.mockObjects()
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) mockObjects() {
|
||||
suite.mockListBuckets = &models.ListBucketsResponse{
|
||||
Buckets: []*models.Bucket{},
|
||||
Total: 0,
|
||||
}
|
||||
suite.mockRemoteBucket = &models.RemoteBucket{
|
||||
AccessKey: swag.String("accessKey"),
|
||||
SecretKey: "secretKey",
|
||||
RemoteARN: swag.String("remoteARN"),
|
||||
Service: "replication",
|
||||
SourceBucket: swag.String("sourceBucket"),
|
||||
TargetBucket: "targetBucket",
|
||||
TargetURL: "targetURL",
|
||||
Status: "",
|
||||
}
|
||||
suite.mockBucketTarget = &madmin.BucketTarget{
|
||||
Credentials: &madmin.Credentials{
|
||||
AccessKey: *suite.mockRemoteBucket.AccessKey,
|
||||
SecretKey: suite.mockRemoteBucket.SecretKey,
|
||||
},
|
||||
Arn: *suite.mockRemoteBucket.RemoteARN,
|
||||
SourceBucket: *suite.mockRemoteBucket.SourceBucket,
|
||||
TargetBucket: suite.mockRemoteBucket.TargetBucket,
|
||||
Endpoint: suite.mockRemoteBucket.TargetURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestRegisterRemoteBucketsHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerAdminBucketRemoteHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.BucketListRemoteBucketsHandler)
|
||||
suite.assert.Nil(api.BucketRemoteBucketDetailsHandler)
|
||||
suite.assert.Nil(api.BucketDeleteRemoteBucketHandler)
|
||||
suite.assert.Nil(api.BucketAddRemoteBucketHandler)
|
||||
suite.assert.Nil(api.BucketSetMultiBucketReplicationHandler)
|
||||
suite.assert.Nil(api.BucketListExternalBucketsHandler)
|
||||
suite.assert.Nil(api.BucketDeleteBucketReplicationRuleHandler)
|
||||
suite.assert.Nil(api.BucketDeleteAllReplicationRulesHandler)
|
||||
suite.assert.Nil(api.BucketDeleteSelectedReplicationRulesHandler)
|
||||
suite.assert.Nil(api.BucketUpdateMultiBucketReplicationHandler)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.BucketListRemoteBucketsHandler)
|
||||
suite.assert.NotNil(api.BucketRemoteBucketDetailsHandler)
|
||||
suite.assert.NotNil(api.BucketDeleteRemoteBucketHandler)
|
||||
suite.assert.NotNil(api.BucketAddRemoteBucketHandler)
|
||||
suite.assert.NotNil(api.BucketSetMultiBucketReplicationHandler)
|
||||
suite.assert.NotNil(api.BucketListExternalBucketsHandler)
|
||||
suite.assert.NotNil(api.BucketDeleteBucketReplicationRuleHandler)
|
||||
suite.assert.NotNil(api.BucketDeleteAllReplicationRulesHandler)
|
||||
suite.assert.NotNil(api.BucketDeleteSelectedReplicationRulesHandler)
|
||||
suite.assert.NotNil(api.BucketUpdateMultiBucketReplicationHandler)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestListRemoteBucketsHandlerWithError() {
|
||||
params, api := suite.initListRemoteBucketsRequest()
|
||||
response := api.BucketListRemoteBucketsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.ListRemoteBucketsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initListRemoteBucketsRequest() (params bucketApi.ListRemoteBucketsParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestListRemoteBucketsWithoutError() {
|
||||
ctx := context.Background()
|
||||
minioListRemoteBucketsMock = func(_ context.Context, _, _ string) (targets []madmin.BucketTarget, err error) {
|
||||
return []madmin.BucketTarget{{
|
||||
Credentials: &madmin.Credentials{
|
||||
AccessKey: "accessKey",
|
||||
SecretKey: "secretKey",
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
res, err := listRemoteBuckets(ctx, &suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestRemoteBucketDetailsHandlerWithError() {
|
||||
params, api := suite.initRemoteBucketDetailsRequest()
|
||||
response := api.BucketRemoteBucketDetailsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.RemoteBucketDetailsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initRemoteBucketDetailsRequest() (params bucketApi.RemoteBucketDetailsParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestGetRemoteBucketWithoutError() {
|
||||
ctx := context.Background()
|
||||
minioGetRemoteBucketMock = func(_ context.Context, _, _ string) (targets *madmin.BucketTarget, err error) {
|
||||
return suite.mockBucketTarget, nil
|
||||
}
|
||||
res, err := getRemoteBucket(ctx, &suite.adminClient, "bucketName")
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Equal(suite.mockRemoteBucket, res)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestDeleteRemoteBucketHandlerWithError() {
|
||||
params, api := suite.initDeleteRemoteBucketRequest()
|
||||
response := api.BucketDeleteRemoteBucketHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.DeleteRemoteBucketDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initDeleteRemoteBucketRequest() (params bucketApi.DeleteRemoteBucketParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestAddRemoteBucketHandlerWithError() {
|
||||
params, api := suite.initAddRemoteBucketRequest()
|
||||
response := api.BucketAddRemoteBucketHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.AddRemoteBucketDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initAddRemoteBucketRequest() (params bucketApi.AddRemoteBucketParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
url := "^&*&^%^"
|
||||
accessKey := "accessKey"
|
||||
secretKey := "secretKey"
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.CreateRemoteBucket{
|
||||
TargetURL: &url,
|
||||
AccessKey: &accessKey,
|
||||
SecretKey: &secretKey,
|
||||
}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestAddRemoteBucketWithoutError() {
|
||||
ctx := context.Background()
|
||||
minioAddRemoteBucketMock = func(_ context.Context, _ string, _ *madmin.BucketTarget) (string, error) {
|
||||
return "bucketName", nil
|
||||
}
|
||||
url := "https://localhost"
|
||||
accessKey := "accessKey"
|
||||
secretKey := "secretKey"
|
||||
targetBucket := "targetBucket"
|
||||
syncMode := "async"
|
||||
sourceBucket := "sourceBucket"
|
||||
data := models.CreateRemoteBucket{
|
||||
TargetURL: &url,
|
||||
TargetBucket: &targetBucket,
|
||||
AccessKey: &accessKey,
|
||||
SecretKey: &secretKey,
|
||||
SyncMode: &syncMode,
|
||||
HealthCheckPeriod: 10,
|
||||
SourceBucket: &sourceBucket,
|
||||
}
|
||||
res, err := addRemoteBucket(ctx, &suite.adminClient, data)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestSetMultiBucketReplicationHandlerWithError() {
|
||||
params, api := suite.initSetMultiBucketReplicationRequest()
|
||||
response := api.BucketSetMultiBucketReplicationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.SetMultiBucketReplicationOK)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initSetMultiBucketReplicationRequest() (params bucketApi.SetMultiBucketReplicationParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
accessKey := "accessKey"
|
||||
secretKey := "secretKey"
|
||||
targetURL := "https://localhost"
|
||||
syncMode := "async"
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.MultiBucketReplication{
|
||||
BucketsRelation: []*models.MultiBucketsRelation{{}},
|
||||
AccessKey: &accessKey,
|
||||
SecretKey: &secretKey,
|
||||
Region: "region",
|
||||
TargetURL: &targetURL,
|
||||
SyncMode: &syncMode,
|
||||
Bandwidth: 10,
|
||||
HealthCheckPeriod: 10,
|
||||
}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestListExternalBucketsHandlerWithError() {
|
||||
params, api := suite.initListExternalBucketsRequest()
|
||||
response := api.BucketListExternalBucketsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.ListExternalBucketsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initListExternalBucketsRequest() (params bucketApi.ListExternalBucketsParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
url := "http://localhost:9000"
|
||||
accessKey := "accessKey"
|
||||
secretKey := "secretKey"
|
||||
tls := false
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.ListExternalBucketsParams{
|
||||
TargetURL: &url,
|
||||
AccessKey: &accessKey,
|
||||
SecretKey: &secretKey,
|
||||
UseTLS: &tls,
|
||||
}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestListExternalBucketsWithError() {
|
||||
ctx := context.Background()
|
||||
minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
|
||||
return madmin.AccountInfo{}, errors.New("error")
|
||||
}
|
||||
res, err := listExternalBuckets(ctx, &suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
suite.assert.Nil(res)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestListExternalBucketsWithoutError() {
|
||||
ctx := context.Background()
|
||||
minioAccountInfoMock = func(_ context.Context) (madmin.AccountInfo, error) {
|
||||
return madmin.AccountInfo{
|
||||
Buckets: []madmin.BucketAccessInfo{},
|
||||
}, nil
|
||||
}
|
||||
res, err := listExternalBuckets(ctx, &suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Equal(suite.mockListBuckets, res)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestDeleteBucketReplicationRuleHandlerWithError() {
|
||||
params, api := suite.initDeleteBucketReplicationRuleRequest()
|
||||
response := api.BucketDeleteBucketReplicationRuleHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.DeleteBucketReplicationRuleDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initDeleteBucketReplicationRuleRequest() (params bucketApi.DeleteBucketReplicationRuleParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestDeleteAllReplicationRulesHandlerWithError() {
|
||||
params, api := suite.initDeleteAllReplicationRulesRequest()
|
||||
response := api.BucketDeleteAllReplicationRulesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.DeleteAllReplicationRulesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initDeleteAllReplicationRulesRequest() (params bucketApi.DeleteAllReplicationRulesParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestDeleteSelectedReplicationRulesHandlerWithError() {
|
||||
params, api := suite.initDeleteSelectedReplicationRulesRequest()
|
||||
response := api.BucketDeleteSelectedReplicationRulesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.DeleteSelectedReplicationRulesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initDeleteSelectedReplicationRulesRequest() (params bucketApi.DeleteSelectedReplicationRulesParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.BucketName = "bucketName"
|
||||
params.Rules = &models.BucketReplicationRuleList{
|
||||
Rules: []string{"rule1", "rule2"},
|
||||
}
|
||||
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) TestUpdateMultiBucketReplicationHandlerWithError() {
|
||||
params, api := suite.initUpdateMultiBucketReplicationRequest()
|
||||
response := api.BucketUpdateMultiBucketReplicationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*bucketApi.UpdateMultiBucketReplicationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *RemoteBucketsTestSuite) initUpdateMultiBucketReplicationRequest() (params bucketApi.UpdateMultiBucketReplicationParams, api operations.ConsoleAPI) {
|
||||
registerAdminBucketRemoteHandlers(&api)
|
||||
r := &http.Request{}
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
rc := r.WithContext(ctx)
|
||||
params.HTTPRequest = rc
|
||||
params.Body = &models.MultiBucketReplicationEdit{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func TestRemoteBuckets(t *testing.T) {
|
||||
suite.Run(t, new(RemoteBucketsTestSuite))
|
||||
}
|
||||
@@ -14,24 +14,24 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
|
||||
svcApi "github.com/minio/console/restapi/operations/service"
|
||||
svcApi "github.com/minio/console/api/operations/service"
|
||||
)
|
||||
|
||||
func registerServiceHandlers(api *operations.ConsoleAPI) {
|
||||
// Restart Service
|
||||
api.ServiceRestartServiceHandler = svcApi.RestartServiceHandlerFunc(func(params svcApi.RestartServiceParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRestartServiceResponse(session, params); err != nil {
|
||||
return svcApi.NewRestartServiceDefault(int(err.Code)).WithPayload(err)
|
||||
return svcApi.NewRestartServiceDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return svcApi.NewRestartServiceNoContent()
|
||||
})
|
||||
@@ -59,10 +59,10 @@ func serviceRestart(ctx context.Context, client MinioAdmin) error {
|
||||
}
|
||||
|
||||
// getRestartServiceResponse performs serviceRestart()
|
||||
func getRestartServiceResponse(session *models.Principal, params svcApi.RestartServiceParams) *models.Error {
|
||||
func getRestartServiceResponse(session *models.Principal, params svcApi.RestartServiceParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
68
api/admin_service_test.go
Normal file
68
api/admin_service_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServiceRestart(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx := context.Background()
|
||||
function := "serviceRestart()"
|
||||
// Test-1 : serviceRestart() restart services no errors
|
||||
// mock function response from listGroups()
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : serviceRestart() returns errors on client.serviceRestart call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3 : serviceRestart() returns errors on client.serverInfo() call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, errors.New("error on server info")
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); assert.Error(err) {
|
||||
assert.Equal("error on server info", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
// shortTraceMsg Short trace record
|
||||
@@ -146,8 +146,7 @@ func shortTrace(info *madmin.ServiceTraceInfo) shortTraceMsg {
|
||||
if host, ok := t.HTTP.ReqInfo.Headers["Host"]; ok {
|
||||
s.Host = strings.Join(host, "")
|
||||
}
|
||||
cSlice := strings.Split(t.HTTP.ReqInfo.Client, ":")
|
||||
s.Client = cSlice[0]
|
||||
s.Client = t.HTTP.ReqInfo.Client
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -22,21 +22,13 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
|
||||
// mock function of listPolicies()
|
||||
func (ac adminClientMock) serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
return minioServiceTraceMock(ctx, threshold, s3, internal, storage, os, errTrace)
|
||||
}
|
||||
|
||||
func TestAdminTrace(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startTraceInfo(ctx, )"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -49,7 +41,7 @@ func TestAdminTrace(t *testing.T) {
|
||||
|
||||
// Test-1: Serve Trace with no errors until trace finishes sending
|
||||
// define mock function behavior for minio server Trace
|
||||
minioServiceTraceMock = func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
minioServiceTraceMock = func(_ context.Context, _ int64, _, _, _, _, _ bool) <-chan madmin.ServiceTraceInfo {
|
||||
ch := make(chan madmin.ServiceTraceInfo)
|
||||
// Only success, start a routine to start reading line by line.
|
||||
go func(ch chan<- madmin.ServiceTraceInfo) {
|
||||
@@ -67,7 +59,7 @@ func TestAdminTrace(t *testing.T) {
|
||||
}
|
||||
writesCount := 1
|
||||
// mock connection WriteMessage() no error
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, data []byte) error {
|
||||
// emulate that receiver gets the message written
|
||||
var t shortTraceMsg
|
||||
_ = json.Unmarshal(data, &t)
|
||||
@@ -92,7 +84,7 @@ func TestAdminTrace(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-2: if error happens while writing, return error
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return fmt.Errorf("error on write")
|
||||
}
|
||||
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
|
||||
@@ -101,7 +93,7 @@ func TestAdminTrace(t *testing.T) {
|
||||
|
||||
// Test-3: error happens on serviceTrace Minio, trace should stop
|
||||
// and error shall be returned.
|
||||
minioServiceTraceMock = func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
minioServiceTraceMock = func(_ context.Context, _ int64, _, _, _, _, _ bool) <-chan madmin.ServiceTraceInfo {
|
||||
ch := make(chan madmin.ServiceTraceInfo)
|
||||
// Only success, start a routine to start reading line by line.
|
||||
go func(ch chan<- madmin.ServiceTraceInfo) {
|
||||
@@ -118,7 +110,7 @@ func TestAdminTrace(t *testing.T) {
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -22,19 +22,15 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
accountApi "github.com/minio/console/api/operations/account"
|
||||
bucketApi "github.com/minio/console/api/operations/bucket"
|
||||
userApi "github.com/minio/console/api/operations/user"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
accountApi "github.com/minio/console/restapi/operations/account"
|
||||
bucketApi "github.com/minio/console/restapi/operations/bucket"
|
||||
userApi "github.com/minio/console/restapi/operations/user"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
// Policy evaluated constants
|
||||
@@ -49,7 +45,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserListUsersHandler = userApi.ListUsersHandlerFunc(func(params userApi.ListUsersParams, session *models.Principal) middleware.Responder {
|
||||
listUsersResponse, err := getListUsersResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewListUsersDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewListUsersDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewListUsersOK().WithPayload(listUsersResponse)
|
||||
})
|
||||
@@ -57,7 +53,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAddUserHandler = userApi.AddUserHandlerFunc(func(params userApi.AddUserParams, session *models.Principal) middleware.Responder {
|
||||
userResponse, err := getUserAddResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewAddUserDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewAddUserDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewAddUserCreated().WithPayload(userResponse)
|
||||
})
|
||||
@@ -65,7 +61,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserRemoveUserHandler = userApi.RemoveUserHandlerFunc(func(params userApi.RemoveUserParams, session *models.Principal) middleware.Responder {
|
||||
err := getRemoveUserResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewRemoveUserDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewRemoveUserDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewRemoveUserNoContent()
|
||||
})
|
||||
@@ -73,7 +69,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserUpdateUserGroupsHandler = userApi.UpdateUserGroupsHandlerFunc(func(params userApi.UpdateUserGroupsParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewUpdateUserGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewUpdateUserGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewUpdateUserGroupsOK().WithPayload(userUpdateResponse)
|
||||
@@ -82,7 +78,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserGetUserInfoHandler = userApi.GetUserInfoHandlerFunc(func(params userApi.GetUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userInfoResponse, err := getUserInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewGetUserInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewGetUserInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewGetUserInfoOK().WithPayload(userInfoResponse)
|
||||
@@ -91,7 +87,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserUpdateUserInfoHandler = userApi.UpdateUserInfoHandlerFunc(func(params userApi.UpdateUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewUpdateUserInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewUpdateUserInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewUpdateUserInfoOK().WithPayload(userUpdateResponse)
|
||||
@@ -100,7 +96,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserBulkUpdateUsersGroupsHandler = userApi.BulkUpdateUsersGroupsHandlerFunc(func(params userApi.BulkUpdateUsersGroupsParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddUsersListToGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewBulkUpdateUsersGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewBulkUpdateUsersGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewBulkUpdateUsersGroupsOK()
|
||||
@@ -108,7 +104,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketListUsersWithAccessToBucketHandler = bucketApi.ListUsersWithAccessToBucketHandlerFunc(func(params bucketApi.ListUsersWithAccessToBucketParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getListUsersWithAccessToBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListUsersWithAccessToBucketDefault(int(err.Code)).WithPayload(err)
|
||||
return bucketApi.NewListUsersWithAccessToBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListUsersWithAccessToBucketOK().WithPayload(response)
|
||||
})
|
||||
@@ -116,7 +112,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AccountChangeUserPasswordHandler = accountApi.ChangeUserPasswordHandlerFunc(func(params accountApi.ChangeUserPasswordParams, session *models.Principal) middleware.Responder {
|
||||
err := getChangeUserPasswordResponse(session, params)
|
||||
if err != nil {
|
||||
return accountApi.NewChangeUserPasswordDefault(int(err.Code)).WithPayload(err)
|
||||
return accountApi.NewChangeUserPasswordDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return accountApi.NewChangeUserPasswordCreated()
|
||||
})
|
||||
@@ -124,7 +120,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserCheckUserServiceAccountsHandler = userApi.CheckUserServiceAccountsHandlerFunc(func(params userApi.CheckUserServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
userSAList, err := getCheckUserSAResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewCheckUserServiceAccountsDefault(int(err.Code)).WithPayload(err)
|
||||
return userApi.NewCheckUserServiceAccountsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewCheckUserServiceAccountsOK().WithPayload(userSAList)
|
||||
})
|
||||
@@ -154,10 +150,10 @@ func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
|
||||
}
|
||||
|
||||
// getListUsersResponse performs listUsers() and serializes it to the handler's output
|
||||
func getListUsersResponse(session *models.Principal, params userApi.ListUsersParams) (*models.ListUsersResponse, *models.Error) {
|
||||
func getListUsersResponse(session *models.Principal, params userApi.ListUsersParams) (*models.ListUsersResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -215,10 +211,10 @@ func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *strin
|
||||
return userRet, nil
|
||||
}
|
||||
|
||||
func getUserAddResponse(session *models.Principal, params userApi.AddUserParams) (*models.User, *models.Error) {
|
||||
func getUserAddResponse(session *models.Principal, params userApi.AddUserParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -252,24 +248,20 @@ func removeUser(ctx context.Context, client MinioAdmin, accessKey string) error
|
||||
return client.removeUser(ctx, accessKey)
|
||||
}
|
||||
|
||||
func getRemoveUserResponse(session *models.Principal, params userApi.RemoveUserParams) *models.Error {
|
||||
func getRemoveUserResponse(session *models.Principal, params userApi.RemoveUserParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
if session.AccountAccessKey == userName {
|
||||
if session.AccountAccessKey == params.Name {
|
||||
return ErrorWithContext(ctx, ErrAvoidSelfAccountDelete)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
if err := removeUser(ctx, adminClient, userName); err != nil {
|
||||
if err := removeUser(ctx, adminClient, params.Name); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
@@ -284,11 +276,11 @@ func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*mad
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoParams) (*models.User, *models.Error) {
|
||||
func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -297,18 +289,13 @@ func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoPa
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
user, err := getUserInfo(ctx, adminClient, userName)
|
||||
user, err := getUserInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
// User doesn't exist, return 404
|
||||
if madmin.ToErrorResponse(err).Code == "XMinioAdminNoSuchUser" {
|
||||
var errorCode int32 = 404
|
||||
errorCode := 404
|
||||
errorMessage := "User doesn't exist"
|
||||
return nil, &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err.Error())}
|
||||
return nil, &CodedAPIError{Code: errorCode, APIError: &models.APIError{Message: errorMessage, DetailedMessage: err.Error()}}
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -337,7 +324,7 @@ func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoPa
|
||||
}
|
||||
|
||||
userInformation := &models.User{
|
||||
AccessKey: userName,
|
||||
AccessKey: params.Name,
|
||||
MemberOf: user.MemberOf,
|
||||
Policy: policies,
|
||||
Status: string(user.Status),
|
||||
@@ -435,11 +422,11 @@ func updateUserGroups(ctx context.Context, client MinioAdmin, user string, group
|
||||
return userReturn, nil
|
||||
}
|
||||
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params userApi.UpdateUserGroupsParams) (*models.User, *models.Error) {
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params userApi.UpdateUserGroupsParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -448,12 +435,7 @@ func getUpdateUserGroupsResponse(session *models.Principal, params userApi.Updat
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
user, err := updateUserGroups(ctx, adminClient, userName, params.Body.Groups)
|
||||
user, err := updateUserGroups(ctx, adminClient, params.Name, params.Body.Groups)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -476,11 +458,11 @@ func setUserStatus(ctx context.Context, client MinioAdmin, user string, status s
|
||||
return client.setUserStatus(ctx, user, setStatus)
|
||||
}
|
||||
|
||||
func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserInfoParams) (*models.User, *models.Error) {
|
||||
func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserInfoParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -488,18 +470,14 @@ func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserI
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
status := *params.Body.Status
|
||||
groups := params.Body.Groups
|
||||
|
||||
if err := setUserStatus(ctx, adminClient, userName, status); err != nil {
|
||||
if err := setUserStatus(ctx, adminClient, params.Name, status); err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
userElem, errUG := updateUserGroups(ctx, adminClient, userName, groups)
|
||||
userElem, errUG := updateUserGroups(ctx, adminClient, params.Name, groups)
|
||||
|
||||
if errUG != nil {
|
||||
return nil, ErrorWithContext(ctx, errUG)
|
||||
@@ -550,11 +528,11 @@ func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.BulkUpdateUsersGroupsParams) *models.Error {
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.BulkUpdateUsersGroupsParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -573,10 +551,10 @@ func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.B
|
||||
return nil
|
||||
}
|
||||
|
||||
func getListUsersWithAccessToBucketResponse(session *models.Principal, params bucketApi.ListUsersWithAccessToBucketParams) ([]string, *models.Error) {
|
||||
func getListUsersWithAccessToBucketResponse(session *models.Principal, params bucketApi.ListUsersWithAccessToBucketParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -684,10 +662,10 @@ func changeUserPassword(ctx context.Context, client MinioAdmin, selectedUser str
|
||||
}
|
||||
|
||||
// getChangeUserPasswordResponse will change the password of selctedUser to newSecretKey
|
||||
func getChangeUserPasswordResponse(session *models.Principal, params accountApi.ChangeUserPasswordParams) *models.Error {
|
||||
func getChangeUserPasswordResponse(session *models.Principal, params accountApi.ChangeUserPasswordParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -706,10 +684,10 @@ func getChangeUserPasswordResponse(session *models.Principal, params accountApi.
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCheckUserSAResponse(session *models.Principal, params userApi.CheckUserServiceAccountsParams) (*models.UserServiceAccountSummary, *models.Error) {
|
||||
func getCheckUserSAResponse(session *models.Principal, params userApi.CheckUserServiceAccountsParams) (*models.UserServiceAccountSummary, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -24,49 +24,14 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioListUsersMock func() (map[string]madmin.UserInfo, error)
|
||||
|
||||
var (
|
||||
minioAddUserMock func(accessKey, secreyKey string) error
|
||||
minioRemoveUserMock func(accessKey string) error
|
||||
minioGetUserInfoMock func(accessKey string) (madmin.UserInfo, error)
|
||||
minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
|
||||
)
|
||||
|
||||
// mock function of listUsers()
|
||||
func (ac adminClientMock) listUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
||||
return minioListUsersMock()
|
||||
}
|
||||
|
||||
// mock function of addUser()
|
||||
func (ac adminClientMock) addUser(ctx context.Context, accessKey, secretKey string) error {
|
||||
return minioAddUserMock(accessKey, secretKey)
|
||||
}
|
||||
|
||||
// mock function of removeUser()
|
||||
func (ac adminClientMock) removeUser(ctx context.Context, accessKey string) error {
|
||||
return minioRemoveUserMock(accessKey)
|
||||
}
|
||||
|
||||
// mock function of getUserInfo()
|
||||
func (ac adminClientMock) getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error) {
|
||||
return minioGetUserInfoMock(accessKey)
|
||||
}
|
||||
|
||||
// mock function of setUserStatus()
|
||||
func (ac adminClientMock) setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error {
|
||||
return minioSetUserStatusMock(accessKey, status)
|
||||
}
|
||||
|
||||
func TestListUsers(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : listUsers() Get response from minio client with two users and return the same number on listUsers()
|
||||
@@ -120,7 +85,7 @@ func TestListUsers(t *testing.T) {
|
||||
|
||||
func TestAddUser(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: valid case of adding a user with a proper access key
|
||||
@@ -137,15 +102,15 @@ func TestAddUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// mock function response from addUser() return no error
|
||||
minioAddUserMock = func(accessKey, secretKey string) error {
|
||||
minioAddUserMock = func(_, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
// Test-1: Add a user
|
||||
@@ -170,7 +135,7 @@ func TestAddUser(t *testing.T) {
|
||||
accessKey = "AB"
|
||||
secretKey = "ABCDEFGHIABCDEFGHI"
|
||||
// mock function response from addUser() return no error
|
||||
minioAddUserMock = func(accessKey, secretKey string) error {
|
||||
minioAddUserMock = func(_, _ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -185,7 +150,7 @@ func TestAddUser(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-4: add groups function returns an error
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -203,14 +168,14 @@ func TestAddUser(t *testing.T) {
|
||||
func TestRemoveUser(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
function := "removeUser()"
|
||||
|
||||
// Test-1: removeUser() delete a user
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioRemoveUserMock = func(accessKey string) error {
|
||||
minioRemoveUserMock = func(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -220,7 +185,7 @@ func TestRemoveUser(t *testing.T) {
|
||||
|
||||
// Test-2: removeUser() make sure errors are handled correctly when error on DeleteUser()
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioRemoveUserMock = func(accessKey string) error {
|
||||
minioRemoveUserMock = func(_ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -232,7 +197,7 @@ func TestRemoveUser(t *testing.T) {
|
||||
func TestUserGroups(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -255,11 +220,11 @@ func TestUserGroups(t *testing.T) {
|
||||
// Test-1: updateUserGroups() updates the groups for a user
|
||||
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
||||
|
||||
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -270,7 +235,7 @@ func TestUserGroups(t *testing.T) {
|
||||
// Test-2: updateUserGroups() make sure errors are handled correctly when error on UpdateGroupMembersMock()
|
||||
// mock function response from removeUser(accessKey)
|
||||
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -279,11 +244,11 @@ func TestUserGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-3: updateUserGroups() make sure we return the correct error when getUserInfo returns error
|
||||
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockEmptyResponse, errors.New("error getting user ")
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -294,7 +259,7 @@ func TestUserGroups(t *testing.T) {
|
||||
|
||||
func TestGetUserInfo(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -314,7 +279,7 @@ func TestGetUserInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
// mock function response from getUserInfo()
|
||||
minioGetUserInfoMock = func(username string) (madmin.UserInfo, error) {
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
function := "getUserInfo()"
|
||||
@@ -329,7 +294,7 @@ func TestGetUserInfo(t *testing.T) {
|
||||
assert.Equal(mockResponse.Status, info.Status)
|
||||
|
||||
// Test-2 : getUserInfo() Return error and see that the error is handled correctly and returned
|
||||
minioGetUserInfoMock = func(username string) (madmin.UserInfo, error) {
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *emptyMockResponse, errors.New("error")
|
||||
}
|
||||
_, err = getUserInfo(ctx, adminClient, userName)
|
||||
@@ -340,7 +305,7 @@ func TestGetUserInfo(t *testing.T) {
|
||||
|
||||
func TestSetUserStatus(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setUserStatus()"
|
||||
userName := "userName123"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -348,7 +313,7 @@ func TestSetUserStatus(t *testing.T) {
|
||||
|
||||
// Test-1: setUserStatus() update valid disabled status
|
||||
expectedStatus := "disabled"
|
||||
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
||||
@@ -356,7 +321,7 @@ func TestSetUserStatus(t *testing.T) {
|
||||
}
|
||||
// Test-2: setUserStatus() update valid enabled status
|
||||
expectedStatus = "enabled"
|
||||
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
||||
@@ -364,7 +329,7 @@ func TestSetUserStatus(t *testing.T) {
|
||||
}
|
||||
// Test-3: setUserStatus() update invalid status, should send error
|
||||
expectedStatus = "invalid"
|
||||
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
||||
@@ -372,7 +337,7 @@ func TestSetUserStatus(t *testing.T) {
|
||||
}
|
||||
// Test-4: setUserStatus() handler error correctly
|
||||
expectedStatus = "enabled"
|
||||
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
||||
@@ -383,7 +348,7 @@ func TestSetUserStatus(t *testing.T) {
|
||||
func TestUserGroupsBulk(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -393,7 +358,7 @@ func TestUserGroupsBulk(t *testing.T) {
|
||||
|
||||
// Test-1: addUsersListToGroups() updates the groups for a users list
|
||||
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -403,7 +368,7 @@ func TestUserGroupsBulk(t *testing.T) {
|
||||
|
||||
// Test-2: addUsersListToGroups() make sure errors are handled correctly when error on updateGroupMembers()
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
@@ -416,7 +381,7 @@ func TestListUsersWithAccessToBucket(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
adminClient := adminClientMock{}
|
||||
adminClient := AdminClientMock{}
|
||||
user1 := madmin.UserInfo{
|
||||
SecretKey: "testtest",
|
||||
PolicyName: "consoleAdmin,testPolicy,redundantPolicy",
|
||||
@@ -562,7 +527,7 @@ func TestListUsersWithAccessToBucket(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
got, _ := listUsersWithAccessToBucket(ctx, adminClient, tt.args.bucket)
|
||||
assert.Equal(got, tt.want)
|
||||
})
|
||||
681
api/client-admin.go
Normal file
681
api/client-admin.go
Normal file
@@ -0,0 +1,681 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
const globalAppName = "MinIO Console"
|
||||
|
||||
// MinioAdmin interface with all functions to be implemented
|
||||
// by mock when testing, it should include all MinioAdmin respective api calls
|
||||
// that are used within this project.
|
||||
type MinioAdmin interface {
|
||||
listUsers(ctx context.Context) (map[string]madmin.UserInfo, error)
|
||||
addUser(ctx context.Context, acessKey, SecretKey string) error
|
||||
removeUser(ctx context.Context, accessKey string) error
|
||||
getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error)
|
||||
setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error
|
||||
listGroups(ctx context.Context) ([]string, error)
|
||||
updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error
|
||||
getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error)
|
||||
setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error
|
||||
listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error)
|
||||
getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error)
|
||||
removePolicy(ctx context.Context, name string) error
|
||||
addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error
|
||||
setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error
|
||||
getConfigKV(ctx context.Context, key string) ([]byte, error)
|
||||
helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error)
|
||||
helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error)
|
||||
setConfigKV(ctx context.Context, kv string) (restart bool, err error)
|
||||
delConfigKV(ctx context.Context, kv string) (err error)
|
||||
serviceRestart(ctx context.Context) error
|
||||
serverInfo(ctx context.Context) (madmin.InfoMessage, error)
|
||||
startProfiling(ctx context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error)
|
||||
serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
AccountInfo(ctx context.Context) (madmin.AccountInfo, error)
|
||||
heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
|
||||
// Service Accounts
|
||||
addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error)
|
||||
listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
|
||||
deleteServiceAccount(ctx context.Context, serviceAccount string) error
|
||||
infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
|
||||
updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
|
||||
// Remote Buckets
|
||||
listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
|
||||
getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
|
||||
removeRemoteBucket(ctx context.Context, bucket, arn string) error
|
||||
addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
|
||||
// Account password management
|
||||
changePassword(ctx context.Context, accessKey, secretKey string) error
|
||||
serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error)
|
||||
// List Tiers
|
||||
listTiers(ctx context.Context) ([]*madmin.TierConfig, error)
|
||||
// Tier Info
|
||||
tierStats(ctx context.Context) ([]madmin.TierInfo, error)
|
||||
// Add Tier
|
||||
addTier(ctx context.Context, tier *madmin.TierConfig) error
|
||||
// Edit Tier Credentials
|
||||
editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error
|
||||
// verify Tier status
|
||||
verifyTierStatus(ctx context.Context, tierName string) error
|
||||
// remove empty Tier
|
||||
removeTier(ctx context.Context, tierName string) error
|
||||
// Speedtest
|
||||
speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error)
|
||||
// Site Relication
|
||||
getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error)
|
||||
addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error)
|
||||
editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error)
|
||||
deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
|
||||
|
||||
// Replication status
|
||||
getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
|
||||
|
||||
// KMS
|
||||
kmsStatus(ctx context.Context) (madmin.KMSStatus, error)
|
||||
kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error)
|
||||
kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error)
|
||||
kmsVersion(ctx context.Context) (*madmin.KMSVersion, error)
|
||||
createKey(ctx context.Context, key string) error
|
||||
listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error)
|
||||
keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error)
|
||||
|
||||
// IDP
|
||||
addOrUpdateIDPConfig(ctx context.Context, idpType, cfgName, cfgData string, update bool) (restart bool, err error)
|
||||
listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error)
|
||||
deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error)
|
||||
getIDPConfig(ctx context.Context, cfgType, cfgName string) (c madmin.IDPConfig, err error)
|
||||
|
||||
// LDAP
|
||||
getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
//
|
||||
// Define the structure of a minIO Client and define the functions that are actually used
|
||||
// from minIO api.
|
||||
type AdminClient struct {
|
||||
Client *madmin.AdminClient
|
||||
}
|
||||
|
||||
func (ac AdminClient) changePassword(ctx context.Context, accessKey, secretKey string) error {
|
||||
return ac.Client.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
|
||||
}
|
||||
|
||||
// implements madmin.ListUsers()
|
||||
func (ac AdminClient) listUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
||||
return ac.Client.ListUsers(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.AddUser()
|
||||
func (ac AdminClient) addUser(ctx context.Context, accessKey, secretKey string) error {
|
||||
return ac.Client.AddUser(ctx, accessKey, secretKey)
|
||||
}
|
||||
|
||||
// implements madmin.RemoveUser()
|
||||
func (ac AdminClient) removeUser(ctx context.Context, accessKey string) error {
|
||||
return ac.Client.RemoveUser(ctx, accessKey)
|
||||
}
|
||||
|
||||
// implements madmin.GetUserInfo()
|
||||
func (ac AdminClient) getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error) {
|
||||
return ac.Client.GetUserInfo(ctx, accessKey)
|
||||
}
|
||||
|
||||
// implements madmin.SetUserStatus()
|
||||
func (ac AdminClient) setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error {
|
||||
return ac.Client.SetUserStatus(ctx, accessKey, status)
|
||||
}
|
||||
|
||||
// implements madmin.ListGroups()
|
||||
func (ac AdminClient) listGroups(ctx context.Context) ([]string, error) {
|
||||
return ac.Client.ListGroups(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.UpdateGroupMembers()
|
||||
func (ac AdminClient) updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error {
|
||||
return ac.Client.UpdateGroupMembers(ctx, greq)
|
||||
}
|
||||
|
||||
// implements madmin.GetGroupDescription(group)
|
||||
func (ac AdminClient) getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error) {
|
||||
return ac.Client.GetGroupDescription(ctx, group)
|
||||
}
|
||||
|
||||
// implements madmin.SetGroupStatus(group, status)
|
||||
func (ac AdminClient) setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error {
|
||||
return ac.Client.SetGroupStatus(ctx, group, status)
|
||||
}
|
||||
|
||||
// implements madmin.ListCannedPolicies()
|
||||
func (ac AdminClient) listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error) {
|
||||
policyMap, err := ac.Client.ListCannedPolicies(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies := make(map[string]*iampolicy.Policy, len(policyMap))
|
||||
for k, v := range policyMap {
|
||||
p, err := iampolicy.ParseConfig(bytes.NewReader(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies[k] = p
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// implements madmin.ListCannedPolicies()
|
||||
func (ac AdminClient) getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error) {
|
||||
info, err := ac.Client.InfoCannedPolicyV2(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iampolicy.ParseConfig(bytes.NewReader(info.Policy))
|
||||
}
|
||||
|
||||
// implements madmin.RemoveCannedPolicy()
|
||||
func (ac AdminClient) removePolicy(ctx context.Context, name string) error {
|
||||
return ac.Client.RemoveCannedPolicy(ctx, name)
|
||||
}
|
||||
|
||||
// implements madmin.AddCannedPolicy()
|
||||
func (ac AdminClient) addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error {
|
||||
buf, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ac.Client.AddCannedPolicy(ctx, name, buf)
|
||||
}
|
||||
|
||||
// implements madmin.SetPolicy()
|
||||
func (ac AdminClient) setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error {
|
||||
// nolint:staticcheck // ignore SA1019
|
||||
return ac.Client.SetPolicy(ctx, policyName, entityName, isGroup)
|
||||
}
|
||||
|
||||
// implements madmin.GetConfigKV()
|
||||
func (ac AdminClient) getConfigKV(ctx context.Context, key string) ([]byte, error) {
|
||||
return ac.Client.GetConfigKV(ctx, key)
|
||||
}
|
||||
|
||||
// implements madmin.HelpConfigKV()
|
||||
func (ac AdminClient) helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return ac.Client.HelpConfigKV(ctx, subSys, key, envOnly)
|
||||
}
|
||||
|
||||
// implements madmin.helpConfigKVGlobal()
|
||||
func (ac AdminClient) helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error) {
|
||||
return ac.Client.HelpConfigKV(ctx, "", "", envOnly)
|
||||
}
|
||||
|
||||
// implements madmin.SetConfigKV()
|
||||
func (ac AdminClient) setConfigKV(ctx context.Context, kv string) (restart bool, err error) {
|
||||
return ac.Client.SetConfigKV(ctx, kv)
|
||||
}
|
||||
|
||||
// implements madmin.DelConfigKV()
|
||||
func (ac AdminClient) delConfigKV(ctx context.Context, kv string) (err error) {
|
||||
_, err = ac.Client.DelConfigKV(ctx, kv)
|
||||
return err
|
||||
}
|
||||
|
||||
// implements madmin.ServiceRestart()
|
||||
func (ac AdminClient) serviceRestart(ctx context.Context) (err error) {
|
||||
return ac.Client.ServiceRestartV2(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.ServerInfo()
|
||||
func (ac AdminClient) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
return ac.Client.ServerInfo(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.StartProfiling()
|
||||
func (ac AdminClient) startProfiling(ctx context.Context, profiler madmin.ProfilerType, duration time.Duration) (io.ReadCloser, error) {
|
||||
return ac.Client.Profile(ctx, profiler, duration)
|
||||
}
|
||||
|
||||
// implements madmin.ServiceTrace()
|
||||
func (ac AdminClient) serviceTrace(ctx context.Context, threshold int64, _, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
thresholdT := time.Duration(threshold)
|
||||
|
||||
tracingOptions := madmin.ServiceTraceOpts{
|
||||
S3: true,
|
||||
OnlyErrors: errTrace,
|
||||
Internal: internal,
|
||||
Storage: storage,
|
||||
OS: os,
|
||||
Threshold: thresholdT,
|
||||
}
|
||||
|
||||
return ac.Client.ServiceTrace(ctx, tracingOptions)
|
||||
}
|
||||
|
||||
// implements madmin.GetLogs()
|
||||
func (ac AdminClient) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return ac.Client.GetLogs(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
// implements madmin.AddServiceAccount()
|
||||
func (ac AdminClient) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error) {
|
||||
return ac.Client.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
|
||||
Policy: []byte(policy),
|
||||
TargetUser: user,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Expiration: expiry,
|
||||
Comment: comment,
|
||||
})
|
||||
}
|
||||
|
||||
// implements madmin.ListServiceAccounts()
|
||||
func (ac AdminClient) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
|
||||
return ac.Client.ListServiceAccounts(ctx, user)
|
||||
}
|
||||
|
||||
// implements madmin.DeleteServiceAccount()
|
||||
func (ac AdminClient) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return ac.Client.DeleteServiceAccount(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
// implements madmin.InfoServiceAccount()
|
||||
func (ac AdminClient) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
|
||||
return ac.Client.InfoServiceAccount(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
// implements madmin.UpdateServiceAccount()
|
||||
func (ac AdminClient) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
|
||||
return ac.Client.UpdateServiceAccount(ctx, serviceAccount, opts)
|
||||
}
|
||||
|
||||
// AccountInfo implements madmin.AccountInfo()
|
||||
func (ac AdminClient) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
|
||||
return ac.Client.AccountInfo(ctx, madmin.AccountOpts{})
|
||||
}
|
||||
|
||||
func (ac AdminClient) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool,
|
||||
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
|
||||
return ac.Client.Heal(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
|
||||
}
|
||||
|
||||
// listRemoteBuckets - return a list of remote buckets
|
||||
func (ac AdminClient) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
|
||||
return ac.Client.ListRemoteTargets(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
// getRemoteBucket - gets remote bucked based on a given bucket name
|
||||
func (ac AdminClient) getRemoteBucket(ctx context.Context, bucket, arnType string) (*madmin.BucketTarget, error) {
|
||||
targets, err := ac.Client.ListRemoteTargets(ctx, bucket, arnType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(targets) > 0 {
|
||||
return &targets[0], nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// removeRemoteBucket removes a remote target associated with particular ARN for this bucket
|
||||
func (ac AdminClient) removeRemoteBucket(ctx context.Context, bucket, arn string) error {
|
||||
return ac.Client.RemoveRemoteTarget(ctx, bucket, arn)
|
||||
}
|
||||
|
||||
// addRemoteBucket sets up a remote target for this bucket
|
||||
func (ac AdminClient) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
|
||||
return ac.Client.SetRemoteTarget(ctx, bucket, target)
|
||||
}
|
||||
|
||||
func (ac AdminClient) setBucketQuota(ctx context.Context, bucket string, quota *madmin.BucketQuota) error {
|
||||
return ac.Client.SetBucketQuota(ctx, bucket, quota)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getBucketQuota(ctx context.Context, bucket string) (madmin.BucketQuota, error) {
|
||||
return ac.Client.GetBucketQuota(ctx, bucket)
|
||||
}
|
||||
|
||||
// serverHealthInfo implements mc.ServerHealthInfo - Connect to a minio server and call Health Info Management API
|
||||
func (ac AdminClient) serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error) {
|
||||
info := madmin.HealthInfo{}
|
||||
var healthInfo interface{}
|
||||
var version string
|
||||
var resp *http.Response
|
||||
var err error
|
||||
resp, version, err = ac.Client.ServerHealthInfo(ctx, madmin.HealthDataTypesList, deadline, "")
|
||||
if err != nil {
|
||||
return nil, version, err
|
||||
}
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
for {
|
||||
if err = decoder.Decode(&info); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if info.Version == "" {
|
||||
return nil, "", ErrHealthReportFail
|
||||
}
|
||||
healthInfo = info
|
||||
|
||||
return healthInfo, version, nil
|
||||
}
|
||||
|
||||
// implements madmin.listTiers()
|
||||
func (ac AdminClient) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
|
||||
return ac.Client.ListTiers(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.tierStats()
|
||||
func (ac AdminClient) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
|
||||
return ac.Client.TierStats(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.AddTier()
|
||||
func (ac AdminClient) addTier(ctx context.Context, cfg *madmin.TierConfig) error {
|
||||
return ac.Client.AddTier(ctx, cfg)
|
||||
}
|
||||
|
||||
// implements madmin.Inspect()
|
||||
func (ac AdminClient) inspect(ctx context.Context, insOpts madmin.InspectOptions) ([]byte, io.ReadCloser, error) {
|
||||
return ac.Client.Inspect(ctx, insOpts)
|
||||
}
|
||||
|
||||
// implements madmin.EditTier()
|
||||
func (ac AdminClient) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
|
||||
return ac.Client.EditTier(ctx, tierName, creds)
|
||||
}
|
||||
|
||||
// implements madmin.VerifyTier()
|
||||
func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) error {
|
||||
return ac.Client.VerifyTier(ctx, tierName)
|
||||
}
|
||||
|
||||
// implements madmin.RemoveTier()
|
||||
func (ac AdminClient) removeTier(ctx context.Context, tierName string) error {
|
||||
return ac.Client.RemoveTier(ctx, tierName)
|
||||
}
|
||||
|
||||
func NewMinioAdminClient(ctx context.Context, sessionClaims *models.Principal) (*madmin.AdminClient, error) {
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
adminClient, err := newAdminFromClaims(sessionClaims, clientIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
return adminClient, nil
|
||||
}
|
||||
|
||||
// newAdminFromClaims creates a minio admin from Decrypted claims using Assume role credentials
|
||||
func newAdminFromClaims(claims *models.Principal, clientIP string) (*madmin.AdminClient, error) {
|
||||
tlsEnabled := getMinIOEndpointIsSecure()
|
||||
endpoint := getMinIOEndpoint()
|
||||
|
||||
adminClient, err := madmin.NewWithOptions(endpoint, &madmin.Options{
|
||||
Creds: credentials.NewStaticV4(claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken),
|
||||
Secure: tlsEnabled,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
adminClient.SetCustomTransport(PrepareSTSClientTransport(clientIP))
|
||||
return adminClient, nil
|
||||
}
|
||||
|
||||
// newAdminFromCreds Creates a minio client using custom credentials for connecting to a remote host
|
||||
func newAdminFromCreds(accessKey, secretKey, endpoint string, tlsEnabled bool) (*madmin.AdminClient, error) {
|
||||
minioClient, err := madmin.NewWithOptions(endpoint, &madmin.Options{
|
||||
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
||||
Secure: tlsEnabled,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
minioClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
return minioClient, nil
|
||||
}
|
||||
|
||||
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||
// that points to the local machine - FQDN are not supported
|
||||
func isLocalIPEndpoint(endpoint string) bool {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return isLocalIPAddress(u.Hostname())
|
||||
}
|
||||
|
||||
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||
// that points to the local machine - FQDN are not supported
|
||||
func isLocalIPAddress(ipAddr string) bool {
|
||||
if ipAddr == "" {
|
||||
return false
|
||||
}
|
||||
if ipAddr == "localhost" {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(ipAddr)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
|
||||
// GetConsoleHTTPClient caches different http clients depending on the target endpoint while taking
|
||||
// in consideration CA certs stored in ${HOME}/.console/certs/CAs and ${HOME}/.minio/certs/CAs
|
||||
// If the target endpoint points to a loopback device, skip the TLS verification.
|
||||
func GetConsoleHTTPClient(clientIP string) *http.Client {
|
||||
return PrepareConsoleHTTPClient(clientIP)
|
||||
}
|
||||
|
||||
var (
|
||||
// De-facto standard header keys.
|
||||
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
||||
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
||||
)
|
||||
|
||||
var (
|
||||
// RFC7239 defines a new "Forwarded: " header designed to replace the
|
||||
// existing use of X-Forwarded-* headers.
|
||||
// e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
|
||||
forwarded = http.CanonicalHeaderKey("Forwarded")
|
||||
// Allows for a sub-match of the first value after 'for=' to the next
|
||||
// comma, semi-colon or space. The match is case-insensitive.
|
||||
forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)(.*)`)
|
||||
)
|
||||
|
||||
// getSourceIPFromHeaders retrieves the IP from the X-Forwarded-For, X-Real-IP
|
||||
// and RFC7239 Forwarded headers (in that order)
|
||||
func getSourceIPFromHeaders(r *http.Request) string {
|
||||
var addr string
|
||||
|
||||
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
||||
// Only grab the first (client) address. Note that '192.168.0.1,
|
||||
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
||||
// the first may represent forwarding proxies earlier in the chain.
|
||||
s := strings.Index(fwd, ", ")
|
||||
if s == -1 {
|
||||
s = len(fwd)
|
||||
}
|
||||
addr = fwd[:s]
|
||||
} else if fwd := r.Header.Get(xRealIP); fwd != "" {
|
||||
// X-Real-IP should only contain one IP address (the client making the
|
||||
// request).
|
||||
addr = fwd
|
||||
} else if fwd := r.Header.Get(forwarded); fwd != "" {
|
||||
// match should contain at least two elements if the protocol was
|
||||
// specified in the Forwarded header. The first element will always be
|
||||
// the 'for=' capture, which we ignore. In the case of multiple IP
|
||||
// addresses (for=8.8.8.8, 8.8.4.4, 172.16.1.20 is valid) we only
|
||||
// extract the first, which should be the client IP.
|
||||
if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
|
||||
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
|
||||
// these quotes.
|
||||
addr = strings.Trim(match[1], `"`)
|
||||
}
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
// getClientIP retrieves the IP from the request headers
|
||||
// and falls back to r.RemoteAddr when necessary.
|
||||
// however returns without bracketing.
|
||||
func getClientIP(r *http.Request) string {
|
||||
addr := getSourceIPFromHeaders(r)
|
||||
if addr == "" {
|
||||
addr = r.RemoteAddr
|
||||
}
|
||||
|
||||
// Default to remote address if headers not set.
|
||||
raddr, _, _ := net.SplitHostPort(addr)
|
||||
if raddr == "" {
|
||||
return addr
|
||||
}
|
||||
return raddr
|
||||
}
|
||||
|
||||
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
||||
return ac.Client.Speedtest(ctx, opts)
|
||||
}
|
||||
|
||||
// Site Replication
|
||||
func (ac AdminClient) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
|
||||
res, err := ac.Client.SiteReplicationInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.SiteReplicationInfo{
|
||||
Enabled: res.Enabled,
|
||||
Name: res.Name,
|
||||
Sites: res.Sites,
|
||||
ServiceAccountAccessKey: res.ServiceAccountAccessKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationAdd(ctx, sites, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &madmin.ReplicateAddStatus{
|
||||
Success: res.Success,
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
InitialSyncErrorMessage: res.InitialSyncErrorMessage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationEdit(ctx, site, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.ReplicateEditStatus{
|
||||
Success: res.Success,
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationRemove(ctx, removeReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.ReplicateRemoveStatus{
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
|
||||
res, err := ac.Client.SRStatusInfo(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsStatus(ctx context.Context) (madmin.KMSStatus, error) {
|
||||
return ac.Client.KMSStatus(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error) {
|
||||
return ac.Client.KMSMetrics(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error) {
|
||||
return ac.Client.KMSAPIs(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsVersion(ctx context.Context) (*madmin.KMSVersion, error) {
|
||||
return ac.Client.KMSVersion(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) createKey(ctx context.Context, key string) error {
|
||||
return ac.Client.CreateKey(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error) {
|
||||
return ac.Client.ListKeys(ctx, pattern)
|
||||
}
|
||||
|
||||
func (ac AdminClient) keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error) {
|
||||
return ac.Client.GetKeyStatus(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) addOrUpdateIDPConfig(ctx context.Context, idpType, cfgName, cfgData string, update bool) (restart bool, err error) {
|
||||
return ac.Client.AddOrUpdateIDPConfig(ctx, idpType, cfgName, cfgData, update)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error) {
|
||||
return ac.Client.ListIDPConfig(ctx, idpType)
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error) {
|
||||
return ac.Client.DeleteIDPConfig(ctx, idpType, cfgName)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getIDPConfig(ctx context.Context, idpType, cfgName string) (c madmin.IDPConfig, err error) {
|
||||
return ac.Client.GetIDPConfig(ctx, idpType, cfgName)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return ac.Client.GetLDAPPolicyEntities(ctx, query)
|
||||
}
|
||||
@@ -14,20 +14,21 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/replication"
|
||||
"github.com/minio/minio-go/v7/pkg/sse"
|
||||
xnet "github.com/minio/pkg/net"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg"
|
||||
@@ -38,7 +39,6 @@ import (
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/lifecycle"
|
||||
"github.com/minio/minio-go/v7/pkg/notification"
|
||||
"github.com/minio/minio-go/v7/pkg/tags"
|
||||
)
|
||||
@@ -74,8 +74,6 @@ type MinioClient interface {
|
||||
setObjectLockConfig(ctx context.Context, bucketName string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error
|
||||
getBucketObjectLockConfig(ctx context.Context, bucketName string) (mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
|
||||
getObjectLockConfig(ctx context.Context, bucketName string) (lock string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, err error)
|
||||
getLifecycleRules(ctx context.Context, bucketName string) (lifecycle *lifecycle.Configuration, err error)
|
||||
setBucketLifecycle(ctx context.Context, bucketName string, config *lifecycle.Configuration) error
|
||||
copyObject(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error)
|
||||
GetBucketTagging(ctx context.Context, bucketName string) (*tags.Tags, error)
|
||||
SetBucketTagging(ctx context.Context, bucketName string, tags *tags.Tags) error
|
||||
@@ -209,14 +207,6 @@ func (c minioClient) getObjectLockConfig(ctx context.Context, bucketName string)
|
||||
return c.client.GetObjectLockConfig(ctx, bucketName)
|
||||
}
|
||||
|
||||
func (c minioClient) getLifecycleRules(ctx context.Context, bucketName string) (lifecycle *lifecycle.Configuration, err error) {
|
||||
return c.client.GetBucketLifecycle(ctx, bucketName)
|
||||
}
|
||||
|
||||
func (c minioClient) setBucketLifecycle(ctx context.Context, bucketName string, config *lifecycle.Configuration) error {
|
||||
return c.client.SetBucketLifecycle(ctx, bucketName, config)
|
||||
}
|
||||
|
||||
func (c minioClient) copyObject(ctx context.Context, dst minio.CopyDestOptions, src minio.CopySrcOptions) (minio.UploadInfo, error) {
|
||||
return c.client.CopyObject(ctx, dst, src)
|
||||
}
|
||||
@@ -232,7 +222,7 @@ type MCClient interface {
|
||||
list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent
|
||||
get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error)
|
||||
shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error)
|
||||
setVersioning(ctx context.Context, status string) *probe.Error
|
||||
setVersioning(ctx context.Context, status string, excludePrefix []string, excludeFolders bool) *probe.Error
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
@@ -265,8 +255,8 @@ func (c mcClient) deleteAllReplicationRules(ctx context.Context) *probe.Error {
|
||||
return c.client.RemoveReplication(ctx)
|
||||
}
|
||||
|
||||
func (c mcClient) setVersioning(ctx context.Context, status string) *probe.Error {
|
||||
return c.client.SetVersion(ctx, status, []string{}, false)
|
||||
func (c mcClient) setVersioning(ctx context.Context, status string, excludePrefix []string, excludeFolders bool) *probe.Error {
|
||||
return c.client.SetVersion(ctx, status, excludePrefix, excludeFolders)
|
||||
}
|
||||
|
||||
func (c mcClient) remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass, forceDelete bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
@@ -278,7 +268,8 @@ func (c mcClient) list(ctx context.Context, opts mc.ListOptions) <-chan *mc.Clie
|
||||
}
|
||||
|
||||
func (c mcClient) get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error) {
|
||||
return c.client.Get(ctx, opts)
|
||||
rd, _, err := c.client.Get(ctx, opts)
|
||||
return rd, err
|
||||
}
|
||||
|
||||
func (c mcClient) shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
@@ -298,6 +289,7 @@ type ConsoleCredentialsI interface {
|
||||
type ConsoleCredentials struct {
|
||||
ConsoleCredentials *credentials.Credentials
|
||||
AccountAccessKey string
|
||||
CredContext *credentials.CredContext
|
||||
}
|
||||
|
||||
func (c ConsoleCredentials) GetAccountAccessKey() string {
|
||||
@@ -306,7 +298,7 @@ func (c ConsoleCredentials) GetAccountAccessKey() string {
|
||||
|
||||
// Get implements *Login.Get()
|
||||
func (c ConsoleCredentials) Get() (credentials.Value, error) {
|
||||
return c.ConsoleCredentials.Get()
|
||||
return c.ConsoleCredentials.GetWithContext(c.CredContext)
|
||||
}
|
||||
|
||||
// Expire implements *Login.Expire()
|
||||
@@ -321,6 +313,10 @@ type consoleSTSAssumeRole struct {
|
||||
stsAssumeRole *credentials.STSAssumeRole
|
||||
}
|
||||
|
||||
func (s consoleSTSAssumeRole) RetrieveWithCredContext(cc *credentials.CredContext) (credentials.Value, error) {
|
||||
return s.stsAssumeRole.RetrieveWithCredContext(cc)
|
||||
}
|
||||
|
||||
func (s consoleSTSAssumeRole) Retrieve() (credentials.Value, error) {
|
||||
return s.stsAssumeRole.Retrieve()
|
||||
}
|
||||
@@ -329,59 +325,88 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
|
||||
return s.stsAssumeRole.IsExpired()
|
||||
}
|
||||
|
||||
func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
||||
func stsCredentials(minioURL, accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) {
|
||||
if accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("credentials endpoint, access and secret key are mandatory for AssumeRoleSTS")
|
||||
}
|
||||
opts := credentials.STSAssumeRoleOptions{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
Location: location,
|
||||
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
||||
}
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
Client: client,
|
||||
STSEndpoint: minioURL,
|
||||
Options: opts,
|
||||
}
|
||||
consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole}
|
||||
return credentials.New(consoleSTSWrapper), nil
|
||||
}
|
||||
|
||||
func NewConsoleCredentials(accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) {
|
||||
minioURL := getMinIOServer()
|
||||
|
||||
// Future authentication methods can be added under this switch statement
|
||||
switch {
|
||||
// LDAP authentication for Console
|
||||
case ldap.GetLDAPEnabled():
|
||||
{
|
||||
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(minioURL), minioURL, accessKey, secretKey)
|
||||
if ldap.GetLDAPEnabled() {
|
||||
creds, err := auth.GetCredentialsFromLDAP(client, minioURL, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credContext := &credentials.CredContext{
|
||||
Client: client,
|
||||
}
|
||||
|
||||
// We verify if LDAP credentials are correct and no error is returned
|
||||
_, err = creds.GetWithContext(credContext)
|
||||
|
||||
if err != nil && strings.Contains(strings.ToLower(err.Error()), "not found") {
|
||||
// We try to use STS Credentials in case LDAP credentials are incorrect.
|
||||
stsCreds, errSTS := stsCredentials(minioURL, accessKey, secretKey, location, client)
|
||||
|
||||
// If there is an error with STS too, then we return the original LDAP error
|
||||
if errSTS != nil {
|
||||
LogError("error in STS credentials for LDAP case: %v ", errSTS)
|
||||
|
||||
// We return LDAP result
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
_, err := stsCreds.GetWithContext(credContext)
|
||||
// There is an error with STS credentials, We return the result of LDAP as STS is not a priority in this case.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return creds, nil
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
// default authentication for Console is via STS (Security Token Service) against MinIO
|
||||
default:
|
||||
{
|
||||
if accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("credentials endpoint, access and secret key are mandatory for AssumeRoleSTS")
|
||||
}
|
||||
opts := credentials.STSAssumeRoleOptions{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
Location: location,
|
||||
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
||||
}
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
Client: GetConsoleHTTPClient(minioURL),
|
||||
STSEndpoint: minioURL,
|
||||
Options: opts,
|
||||
}
|
||||
consoleSTSWrapper := consoleSTSAssumeRole{stsAssumeRole: stsAssumeRole}
|
||||
return credentials.New(consoleSTSWrapper), nil
|
||||
|
||||
return stsCreds, nil
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
return stsCredentials(minioURL, accessKey, secretKey, location, client)
|
||||
}
|
||||
|
||||
// getConsoleCredentialsFromSession returns the *consoleCredentials.Login associated to the
|
||||
// provided session token, this is useful for running the Expire() or IsExpired() operations
|
||||
func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Credentials {
|
||||
if claims == nil {
|
||||
return credentials.NewStaticV4("", "", "")
|
||||
}
|
||||
return credentials.NewStaticV4(claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken)
|
||||
}
|
||||
|
||||
// newMinioClient creates a new MinIO client based on the ConsoleCredentials extracted
|
||||
// from the provided session token
|
||||
func newMinioClient(claims *models.Principal) (*minio.Client, error) {
|
||||
func newMinioClient(claims *models.Principal, clientIP string) (*minio.Client, error) {
|
||||
creds := getConsoleCredentialsFromSession(claims)
|
||||
endpoint := getMinIOEndpoint()
|
||||
secure := getMinIOEndpointIsSecure()
|
||||
minioClient, err := minio.New(endpoint, &minio.Options{
|
||||
Creds: creds,
|
||||
Secure: secure,
|
||||
Transport: GetConsoleHTTPClient(getMinIOServer()).Transport,
|
||||
Transport: GetConsoleHTTPClient(clientIP).Transport,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -393,10 +418,9 @@ func newMinioClient(claims *models.Principal) (*minio.Client, error) {
|
||||
|
||||
// computeObjectURLWithoutEncode returns a MinIO url containing the object filename without encoding
|
||||
func computeObjectURLWithoutEncode(bucketName, prefix string) (string, error) {
|
||||
endpoint := getMinIOServer()
|
||||
u, err := xnet.ParseHTTPURL(endpoint)
|
||||
u, err := xnet.ParseHTTPURL(getMinIOServer())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the provided endpoint is invalid")
|
||||
return "", fmt.Errorf("the provided endpoint: '%s' is invalid", getMinIOServer())
|
||||
}
|
||||
var p string
|
||||
if strings.TrimSpace(bucketName) != "" {
|
||||
@@ -405,11 +429,11 @@ func computeObjectURLWithoutEncode(bucketName, prefix string) (string, error) {
|
||||
if strings.TrimSpace(prefix) != "" {
|
||||
p = pathJoinFinalSlash(p, prefix)
|
||||
}
|
||||
return fmt.Sprintf("%s://%s/%s", u.Scheme, u.Host, p), nil
|
||||
return u.String() + "/" + p, nil
|
||||
}
|
||||
|
||||
// newS3BucketClient creates a new mc S3Client to talk to the server based on a bucket
|
||||
func newS3BucketClient(claims *models.Principal, bucketName string, prefix string) (*mc.S3Client, error) {
|
||||
func newS3BucketClient(claims *models.Principal, bucketName string, prefix string, clientIP string) (*mc.S3Client, error) {
|
||||
if claims == nil {
|
||||
return nil, fmt.Errorf("the provided credentials are invalid")
|
||||
}
|
||||
@@ -418,7 +442,7 @@ func newS3BucketClient(claims *models.Principal, bucketName string, prefix strin
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the provided endpoint is invalid")
|
||||
}
|
||||
s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken)
|
||||
s3Config := newS3Config(objectURL, claims.STSAccessKeyID, claims.STSSecretAccessKey, claims.STSSessionToken, clientIP)
|
||||
client, pErr := mc.S3New(s3Config)
|
||||
if pErr != nil {
|
||||
return nil, pErr.Cause
|
||||
@@ -440,27 +464,24 @@ func pathJoinFinalSlash(elem ...string) string {
|
||||
return path.Join(elem...)
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
// newS3Config simply creates a new Config struct using the passed
|
||||
// parameters.
|
||||
func newS3Config(endpoint, accessKey, secretKey, sessionToken string) *mc.Config {
|
||||
func newS3Config(endpoint, accessKey, secretKey, sessionToken string, clientIP string) *mc.Config {
|
||||
// We have a valid alias and hostConfig. We populate the/
|
||||
// consoleCredentials from the match found in the config file.
|
||||
s3Config := new(mc.Config)
|
||||
|
||||
s3Config.AppName = globalAppName
|
||||
s3Config.AppVersion = pkg.Version
|
||||
s3Config.Debug = false
|
||||
|
||||
s3Config.HostURL = endpoint
|
||||
s3Config.AccessKey = accessKey
|
||||
s3Config.SecretKey = secretKey
|
||||
s3Config.SessionToken = sessionToken
|
||||
s3Config.Signature = "S3v4"
|
||||
|
||||
insecure := isLocalIPEndpoint(endpoint)
|
||||
|
||||
s3Config.Insecure = insecure
|
||||
s3Config.Transport = PrepareSTSClientTransport(insecure)
|
||||
|
||||
return s3Config
|
||||
return &mc.Config{
|
||||
HostURL: endpoint,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
SessionToken: sessionToken,
|
||||
Signature: "S3v4",
|
||||
AppName: globalAppName,
|
||||
AppVersion: pkg.Version,
|
||||
Insecure: isLocalIPEndpoint(endpoint),
|
||||
Transport: &ConsoleTransport{
|
||||
ClientIP: clientIP,
|
||||
Transport: GlobalTransport,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
// Copyright (c) 2024 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import "testing"
|
||||
|
||||
@@ -76,14 +76,15 @@ func Test_computeObjectURLWithoutEncode(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
got, err := computeObjectURLWithoutEncode(tt.args.bucketName, tt.args.prefix)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("computeObjectURLWithoutEncode() errors = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("computeObjectURLWithoutEncode() got = %v, want %v", got, tt.want)
|
||||
if err == nil {
|
||||
if got != tt.want {
|
||||
t.Errorf("computeObjectURLWithoutEncode() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
305
api/config.go
Normal file
305
api/config.go
Normal file
@@ -0,0 +1,305 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
xcerts "github.com/minio/pkg/v3/certs"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
)
|
||||
|
||||
var (
|
||||
// Port console default port
|
||||
Port = "9090"
|
||||
|
||||
// Hostname console hostname
|
||||
// avoid listening on 0.0.0.0 by default
|
||||
// instead listen on all IPv4 and IPv6
|
||||
// - Hostname should be empty.
|
||||
Hostname = ""
|
||||
|
||||
// TLSPort console tls port
|
||||
TLSPort = "9443"
|
||||
|
||||
// TLSRedirect console tls redirect rule
|
||||
TLSRedirect = "on"
|
||||
|
||||
ConsoleResourceName = "console-ui"
|
||||
)
|
||||
|
||||
var (
|
||||
// GlobalRootCAs is CA root certificates, a nil value means system certs pool will be used
|
||||
GlobalRootCAs *x509.CertPool
|
||||
// GlobalPublicCerts has certificates Console will use to serve clients
|
||||
GlobalPublicCerts []*x509.Certificate
|
||||
// GlobalTLSCertsManager custom TLS Manager for SNI support
|
||||
GlobalTLSCertsManager *xcerts.Manager
|
||||
// GlobalTransport is common transport used for all HTTP calls, this is set via
|
||||
// MinIO server to be the correct transport, however we still define some defaults
|
||||
// here just in case.
|
||||
GlobalTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
KeepAlive: 15 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 1024,
|
||||
MaxIdleConnsPerHost: 1024,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 10 * time.Second,
|
||||
DisableCompression: true, // Set to avoid auto-decompression
|
||||
TLSClientConfig: &tls.Config{
|
||||
// Can't use SSLv3 because of POODLE and BEAST
|
||||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
||||
MinVersion: tls.VersionTLS12,
|
||||
// Console runs in the same pod/node as MinIO this is acceptable.
|
||||
InsecureSkipVerify: true,
|
||||
RootCAs: GlobalRootCAs,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// MinIOConfig represents application configuration passed in from the MinIO
|
||||
// server to the console.
|
||||
type MinIOConfig struct {
|
||||
OpenIDProviders oauth2.OpenIDPCfg
|
||||
}
|
||||
|
||||
// GlobalMinIOConfig is the global application configuration passed in from the
|
||||
// MinIO server.
|
||||
var GlobalMinIOConfig MinIOConfig
|
||||
|
||||
func getMinIOServer() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIOServer, "http://localhost:9000"))
|
||||
}
|
||||
|
||||
func GetMinIORegion() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIORegion, ""))
|
||||
}
|
||||
|
||||
func getMinIOEndpoint() string {
|
||||
u, err := xnet.ParseHTTPURL(getMinIOServer())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u.Host
|
||||
}
|
||||
|
||||
func getMinIOEndpointIsSecure() bool {
|
||||
u, err := xnet.ParseHTTPURL(getMinIOServer())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u.Scheme == "https"
|
||||
}
|
||||
|
||||
// GetHostname gets console hostname set on env variable,
|
||||
// default one or defined on run command
|
||||
func GetHostname() string {
|
||||
return strings.ToLower(env.Get(ConsoleHostname, Hostname))
|
||||
}
|
||||
|
||||
// GetPort gets console por set on env variable
|
||||
// or default one
|
||||
func GetPort() int {
|
||||
port, err := strconv.Atoi(env.Get(ConsolePort, Port))
|
||||
if err != nil {
|
||||
port = 9090
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
// GetTLSPort gets console tls port set on env variable
|
||||
// or default one
|
||||
func GetTLSPort() int {
|
||||
port, err := strconv.Atoi(env.Get(ConsoleTLSPort, TLSPort))
|
||||
if err != nil {
|
||||
port = 9443
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
// If GetTLSRedirect is set to true, then only allow HTTPS requests. Default is true.
|
||||
func GetTLSRedirect() string {
|
||||
return strings.ToLower(env.Get(ConsoleSecureTLSRedirect, TLSRedirect))
|
||||
}
|
||||
|
||||
// Get secure middleware env variable configurations
|
||||
func GetSecureAllowedHosts() []string {
|
||||
allowedHosts := env.Get(ConsoleSecureAllowedHosts, "")
|
||||
if allowedHosts != "" {
|
||||
return strings.Split(allowedHosts, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// AllowedHostsAreRegex determines, if the provided AllowedHosts slice contains valid regular expressions. Default is false.
|
||||
func GetSecureAllowedHostsAreRegex() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureAllowedHostsAreRegex, "off")) == "on"
|
||||
}
|
||||
|
||||
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is true.
|
||||
func GetSecureFrameDeny() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureFrameDeny, "on")) == "on"
|
||||
}
|
||||
|
||||
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is true.
|
||||
func GetSecureContentTypeNonSniff() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureContentTypeNoSniff, "on")) == "on"
|
||||
}
|
||||
|
||||
// If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is true.
|
||||
func GetSecureBrowserXSSFilter() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureBrowserXSSFilter, "on")) == "on"
|
||||
}
|
||||
|
||||
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
// Passing a template string will replace `$NONCE` with a dynamic nonce value of 16 bytes for each request which can be
|
||||
// later retrieved using the Nonce function.
|
||||
func GetSecureContentSecurityPolicy() string {
|
||||
return env.Get(ConsoleSecureContentSecurityPolicy, "")
|
||||
}
|
||||
|
||||
// ContentSecurityPolicyReportOnly allows the Content-Security-Policy-Report-Only header value to be set with a custom value. Default is "".
|
||||
func GetSecureContentSecurityPolicyReportOnly() string {
|
||||
return env.Get(ConsoleSecureContentSecurityPolicyReportOnly, "")
|
||||
}
|
||||
|
||||
// HostsProxyHeaders is a set of header keys that may hold a proxied hostname value for the request.
|
||||
func GetSecureHostsProxyHeaders() []string {
|
||||
allowedHosts := env.Get(ConsoleSecureHostsProxyHeaders, "")
|
||||
if allowedHosts != "" {
|
||||
return strings.Split(allowedHosts, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// TLSHost is the host name that is used to redirect HTTP requests to HTTPS. Default is "", which indicates to use the same host.
|
||||
func GetSecureTLSHost() string {
|
||||
tlsHost := env.Get(ConsoleSecureTLSHost, "")
|
||||
if tlsHost == "" && Hostname != "" {
|
||||
return net.JoinHostPort(Hostname, TLSPort)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
func GetSecureSTSSeconds() int64 {
|
||||
seconds, err := strconv.Atoi(env.Get(ConsoleSecureSTSSeconds, "0"))
|
||||
if err != nil {
|
||||
seconds = 0
|
||||
}
|
||||
return int64(seconds)
|
||||
}
|
||||
|
||||
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
|
||||
func GetSecureSTSIncludeSubdomains() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureSTSIncludeSubdomains, "off")) == "on"
|
||||
}
|
||||
|
||||
// If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
|
||||
func GetSecureSTSPreload() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureSTSPreload, "off")) == "on"
|
||||
}
|
||||
|
||||
// If TLSTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
|
||||
func GetSecureTLSTemporaryRedirect() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureTLSTemporaryRedirect, "off")) == "on"
|
||||
}
|
||||
|
||||
// STS header is only included when the connection is HTTPS.
|
||||
func GetSecureForceSTSHeader() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureForceSTSHeader, "off")) == "on"
|
||||
}
|
||||
|
||||
// ReferrerPolicy allows the Referrer-Policy header with the value to be set with a custom value. Default is "".
|
||||
func GetSecureReferrerPolicy() string {
|
||||
return env.Get(ConsoleSecureReferrerPolicy, "")
|
||||
}
|
||||
|
||||
// FeaturePolicy allows the Feature-Policy header with the value to be set with a custom value. Default is "".
|
||||
func GetSecureFeaturePolicy() string {
|
||||
return env.Get(ConsoleSecureFeaturePolicy, "")
|
||||
}
|
||||
|
||||
func getLogSearchAPIToken() string {
|
||||
if v := env.Get(ConsoleLogQueryAuthToken, ""); v != "" {
|
||||
return v
|
||||
}
|
||||
return env.Get(LogSearchQueryAuthToken, "")
|
||||
}
|
||||
|
||||
func getLogSearchURL() string {
|
||||
return env.Get(ConsoleLogQueryURL, "")
|
||||
}
|
||||
|
||||
func getPrometheusURL() string {
|
||||
return env.Get(PrometheusURL, "")
|
||||
}
|
||||
|
||||
func getPrometheusAuthToken() string {
|
||||
return env.Get(PrometheusAuthToken, "")
|
||||
}
|
||||
|
||||
func getPrometheusJobID() string {
|
||||
return env.Get(PrometheusJobID, "minio-job")
|
||||
}
|
||||
|
||||
func getPrometheusExtraLabels() string {
|
||||
return env.Get(PrometheusExtraLabels, "")
|
||||
}
|
||||
|
||||
func getMaxConcurrentUploadsLimit() int64 {
|
||||
cu, err := strconv.ParseInt(env.Get(ConsoleMaxConcurrentUploads, "10"), 10, 64)
|
||||
if err != nil {
|
||||
return 10
|
||||
}
|
||||
|
||||
return cu
|
||||
}
|
||||
|
||||
func getMaxConcurrentDownloadsLimit() int64 {
|
||||
cu, err := strconv.ParseInt(env.Get(ConsoleMaxConcurrentDownloads, "20"), 10, 64)
|
||||
if err != nil {
|
||||
return 20
|
||||
}
|
||||
|
||||
return cu
|
||||
}
|
||||
|
||||
func getConsoleDevMode() bool {
|
||||
return strings.ToLower(env.Get(ConsoleDevMode, "off")) == "on"
|
||||
}
|
||||
|
||||
func getConsoleAnimatedLogin() bool {
|
||||
return strings.ToLower(env.Get(ConsoleAnimatedLogin, "on")) == "on"
|
||||
}
|
||||
|
||||
func getConsoleBrowserRedirectURL() string {
|
||||
return env.Get(ConsoleBrowserRedirectURL, "")
|
||||
}
|
||||
393
api/config_test.go
Normal file
393
api/config_test.go
Normal file
@@ -0,0 +1,393 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetHostname(t *testing.T) {
|
||||
os.Setenv(ConsoleHostname, "x")
|
||||
defer os.Unsetenv(ConsoleHostname)
|
||||
assert.Equalf(t, "x", GetHostname(), "GetHostname()")
|
||||
}
|
||||
|
||||
func TestGetPort(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "valid port",
|
||||
args: args{
|
||||
env: "9091",
|
||||
},
|
||||
want: 9091,
|
||||
},
|
||||
{
|
||||
name: "invalid port",
|
||||
args: args{
|
||||
env: "duck",
|
||||
},
|
||||
want: 9090,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsolePort, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetPort(), "GetPort()")
|
||||
os.Unsetenv(ConsolePort)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTLSPort(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "valid port",
|
||||
args: args{
|
||||
env: "9444",
|
||||
},
|
||||
want: 9444,
|
||||
},
|
||||
{
|
||||
name: "invalid port",
|
||||
args: args{
|
||||
env: "duck",
|
||||
},
|
||||
want: 9443,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleTLSPort, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetTLSPort(), "GetTLSPort()")
|
||||
os.Unsetenv(ConsoleTLSPort)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSecureAllowedHosts(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid hosts",
|
||||
args: args{
|
||||
env: "host1,host2",
|
||||
},
|
||||
want: []string{"host1", "host2"},
|
||||
},
|
||||
{
|
||||
name: "empty hosts",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureAllowedHosts, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureAllowedHosts(), "GetSecureAllowedHosts()")
|
||||
os.Unsetenv(ConsoleSecureAllowedHosts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSecureHostsProxyHeaders(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "valid headers",
|
||||
args: args{
|
||||
env: "header1,header2",
|
||||
},
|
||||
want: []string{"header1", "header2"},
|
||||
},
|
||||
{
|
||||
name: "empty headers",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureHostsProxyHeaders, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureHostsProxyHeaders(), "GetSecureHostsProxyHeaders()")
|
||||
os.Unsetenv(ConsoleSecureHostsProxyHeaders)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSecureSTSSeconds(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int64
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
env: "1",
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
args: args{
|
||||
env: "duck",
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureSTSSeconds, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureSTSSeconds(), "GetSecureSTSSeconds()")
|
||||
os.Unsetenv(ConsoleSecureSTSSeconds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getLogSearchAPIToken(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "env set",
|
||||
args: args{
|
||||
env: "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "env not set",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleLogQueryAuthToken, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getLogSearchAPIToken(), "getLogSearchAPIToken()")
|
||||
os.Setenv(ConsoleLogQueryAuthToken, tt.args.env)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getPrometheusURL(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "env set",
|
||||
args: args{
|
||||
env: "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "env not set",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(PrometheusURL, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getPrometheusURL(), "getPrometheusURL()")
|
||||
os.Setenv(PrometheusURL, tt.args.env)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getPrometheusJobID(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "env set",
|
||||
args: args{
|
||||
env: "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "env not set",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: "minio-job",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(PrometheusJobID, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getPrometheusJobID(), "getPrometheusJobID()")
|
||||
os.Setenv(PrometheusJobID, tt.args.env)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getMaxConcurrentUploadsLimit(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int64
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
env: "1",
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
args: args{
|
||||
env: "duck",
|
||||
},
|
||||
want: 10,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleMaxConcurrentUploads, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getMaxConcurrentUploadsLimit(), "getMaxConcurrentUploadsLimit()")
|
||||
os.Unsetenv(ConsoleMaxConcurrentUploads)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getMaxConcurrentDownloadsLimit(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int64
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
env: "1",
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
args: args{
|
||||
env: "duck",
|
||||
},
|
||||
want: 20,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleMaxConcurrentDownloads, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getMaxConcurrentDownloadsLimit(), "getMaxConcurrentDownloadsLimit()")
|
||||
os.Unsetenv(ConsoleMaxConcurrentDownloads)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getConsoleDevMode(t *testing.T) {
|
||||
type args struct {
|
||||
env string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "value set",
|
||||
args: args{
|
||||
env: "on",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "value not set",
|
||||
args: args{
|
||||
env: "",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleDevMode, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getConsoleDevMode(), "getConsoleDevMode()")
|
||||
os.Unsetenv(ConsoleDevMode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
// This file is safe to edit. Once it exists it will not be overwritten
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -31,26 +31,30 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
|
||||
portal_ui "github.com/minio/console/portal-ui"
|
||||
"github.com/minio/pkg/env"
|
||||
"github.com/minio/pkg/mimedb"
|
||||
xnet "github.com/minio/pkg/net"
|
||||
portal_ui "github.com/minio/console/web-app"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
"github.com/minio/pkg/v3/mimedb"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/auth"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/unrolled/secure"
|
||||
)
|
||||
|
||||
@@ -65,7 +69,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
subPath = "/"
|
||||
cfgSubPath = "/"
|
||||
subPathOnce sync.Once
|
||||
)
|
||||
|
||||
@@ -80,9 +84,12 @@ func configureFlags(api *operations.ConsoleAPI) {
|
||||
|
||||
func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
// Applies when the "x-token" header is set
|
||||
api.KeyAuth = func(token string, scopes []string) (*models.Principal, error) {
|
||||
api.KeyAuth = func(token string, _ []string) (*models.Principal, error) {
|
||||
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
|
||||
// was generated and signed by us in the first place
|
||||
if token == "Anonymous" {
|
||||
return &models.Principal{}, nil
|
||||
}
|
||||
claims, err := auth.ParseClaimsFromToken(token)
|
||||
if err != nil {
|
||||
api.Logger("Unable to validate the session token %s: %v", token, err)
|
||||
@@ -98,6 +105,9 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
CustomStyleOb: claims.CustomStyleOB,
|
||||
}, nil
|
||||
}
|
||||
api.AnonymousAuth = func(_ string) (*models.Principal, error) {
|
||||
return &models.Principal{}, nil
|
||||
}
|
||||
|
||||
// Register login handlers
|
||||
registerLoginHandlers(api)
|
||||
@@ -115,14 +125,10 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerConfigHandlers(api)
|
||||
// Register bucket events handlers
|
||||
registerBucketEventsHandlers(api)
|
||||
// Register bucket lifecycle handlers
|
||||
registerBucketsLifecycleHandlers(api)
|
||||
// Register service handlers
|
||||
registerServiceHandlers(api)
|
||||
// Register session handlers
|
||||
registerSessionHandlers(api)
|
||||
// Register version handlers
|
||||
registerVersionHandlers(api)
|
||||
// Register admin info handlers
|
||||
registerAdminInfoHandlers(api)
|
||||
// Register admin arns handlers
|
||||
@@ -135,22 +141,15 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerAdminBucketRemoteHandlers(api)
|
||||
// Register admin log search
|
||||
registerLogSearchHandlers(api)
|
||||
// Register admin subnet handlers
|
||||
registerSubnetHandlers(api)
|
||||
// Register admin KMS handlers
|
||||
registerKMSHandlers(api)
|
||||
// Register admin IDP handlers
|
||||
registerIDPHandlers(api)
|
||||
// Register Account handlers
|
||||
registerAdminTiersHandlers(api)
|
||||
// Register Inspect Handler
|
||||
registerInspectHandler(api)
|
||||
// Register nodes handlers
|
||||
registerNodesHandler(api)
|
||||
|
||||
registerSiteReplicationHandler(api)
|
||||
registerSiteReplicationStatusHandler(api)
|
||||
|
||||
// Operator Console
|
||||
|
||||
// Register Object's Handlers
|
||||
@@ -160,6 +159,10 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
// Register Account handlers
|
||||
registerAccountHandlers(api)
|
||||
|
||||
registerReleasesHandlers(api)
|
||||
|
||||
registerPublicObjectsHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
api.ServerShutdown = func() {}
|
||||
@@ -181,15 +184,12 @@ func setupMiddlewares(handler http.Handler) http.Handler {
|
||||
|
||||
func ContextMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requestID, err := utils.NewUUID()
|
||||
if err != nil && err != auth.ErrNoAuthToken {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
requestID := uuid.NewString()
|
||||
ctx := context.WithValue(r.Context(), utils.ContextRequestID, requestID)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestUserAgent, r.UserAgent())
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestHost, r.Host)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestRemoteAddr, r.RemoteAddr)
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(r))
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
@@ -204,6 +204,97 @@ func AuditLogMiddleware(next http.Handler) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func DebugLogMiddleware(next http.Handler) http.Handler {
|
||||
debugLogLevel, _ := env.GetInt("CONSOLE_DEBUG_LOGLEVEL", 0)
|
||||
if debugLogLevel == 0 {
|
||||
return next
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rw := logger.NewResponseWriter(w)
|
||||
next.ServeHTTP(rw, r)
|
||||
debugLog(debugLogLevel, r, rw)
|
||||
})
|
||||
}
|
||||
|
||||
func debugLog(debugLogLevel int, r *http.Request, rw *logger.ResponseWriter) {
|
||||
switch debugLogLevel {
|
||||
case 1:
|
||||
// Log server errors only (summary)
|
||||
if rw.StatusCode >= 500 {
|
||||
debugLogSummary(r, rw)
|
||||
}
|
||||
case 2:
|
||||
// Log server and client errors (summary)
|
||||
if rw.StatusCode >= 400 {
|
||||
debugLogSummary(r, rw)
|
||||
}
|
||||
case 3:
|
||||
// Log all requests (summary)
|
||||
debugLogSummary(r, rw)
|
||||
case 4:
|
||||
// Log server errors only (including headers)
|
||||
if rw.StatusCode >= 500 {
|
||||
debugLogDetails(r, rw)
|
||||
}
|
||||
case 5:
|
||||
// Log server and client errors (including headers)
|
||||
if rw.StatusCode >= 400 {
|
||||
debugLogDetails(r, rw)
|
||||
}
|
||||
case 6:
|
||||
// Log all requests (including headers)
|
||||
debugLogDetails(r, rw)
|
||||
}
|
||||
}
|
||||
|
||||
func debugLogSummary(r *http.Request, rw *logger.ResponseWriter) {
|
||||
statusCode := strconv.Itoa(rw.StatusCode)
|
||||
if rw.Hijacked {
|
||||
statusCode = "hijacked"
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s %s %s %s %dms", r.RemoteAddr, r.Method, r.URL, statusCode, time.Since(rw.StartTime).Milliseconds()))
|
||||
}
|
||||
|
||||
func debugLogDetails(r *http.Request, rw *logger.ResponseWriter) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("- Method/URL: %s %s\n", r.Method, r.URL))
|
||||
sb.WriteString(fmt.Sprintf(" Remote endpoint: %s\n", r.RemoteAddr))
|
||||
if rw.Hijacked {
|
||||
sb.WriteString(" Status code: <hijacked, probably a websocket>\n")
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf(" Status code: %d\n", rw.StatusCode))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" Duration (ms): %d\n", time.Since(rw.StartTime).Milliseconds()))
|
||||
sb.WriteString(" Request headers: ")
|
||||
debugLogHeaders(&sb, r.Header)
|
||||
sb.WriteString(" Response headers: ")
|
||||
debugLogHeaders(&sb, rw.Header())
|
||||
logger.Info(sb.String())
|
||||
}
|
||||
|
||||
func debugLogHeaders(sb *strings.Builder, h http.Header) {
|
||||
keys := make([]string, 0, len(h))
|
||||
for key := range h {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
first := true
|
||||
for _, key := range keys {
|
||||
values := h[key]
|
||||
for _, value := range values {
|
||||
if !first {
|
||||
sb.WriteString(" ")
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%s: %s\n", key, value))
|
||||
}
|
||||
}
|
||||
if first {
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
|
||||
// So this is a good place to plug in a panic handling middleware, logger and metrics
|
||||
func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
@@ -216,6 +307,8 @@ func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
next = ContextMiddleware(next)
|
||||
// handle cookie or authorization header for session
|
||||
next = AuthenticationMiddleware(next)
|
||||
// handle debug logging
|
||||
next = DebugLogMiddleware(next)
|
||||
|
||||
sslHostFn := secure.SSLHostFunc(func(host string) string {
|
||||
xhost, err := xnet.ParseHost(host)
|
||||
@@ -244,10 +337,8 @@ func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
BrowserXssFilter: GetSecureBrowserXSSFilter(),
|
||||
ContentSecurityPolicy: GetSecureContentSecurityPolicy(),
|
||||
ContentSecurityPolicyReportOnly: GetSecureContentSecurityPolicyReportOnly(),
|
||||
PublicKey: GetSecurePublicKey(),
|
||||
ReferrerPolicy: GetSecureReferrerPolicy(),
|
||||
FeaturePolicy: GetSecureFeaturePolicy(),
|
||||
ExpectCTHeader: GetSecureExpectCTHeader(),
|
||||
IsDevelopment: false,
|
||||
}
|
||||
secureMiddleware := secure.New(secureOptions)
|
||||
@@ -288,6 +379,8 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
// handle it appropriately.
|
||||
if len(sessionToken) > 0 {
|
||||
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", string(sessionToken)))
|
||||
} else {
|
||||
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", "Anonymous"))
|
||||
}
|
||||
ctx := r.Context()
|
||||
claims, _ := auth.ParseClaimsFromToken(string(sessionToken))
|
||||
@@ -301,6 +394,12 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
|
||||
// FileServerMiddleware serves files from the static folder
|
||||
func FileServerMiddleware(next http.Handler) http.Handler {
|
||||
buildFs, err := fs.Sub(portal_ui.GetStaticAssets(), "build")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
spaFileHandler := wrapHandlerSinglePageApplication(requestBounce(http.FileServer(http.FS(buildFs))))
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Server", globalAppName) // do not add version information
|
||||
switch {
|
||||
@@ -309,11 +408,7 @@ func FileServerMiddleware(next http.Handler) http.Handler {
|
||||
case strings.HasPrefix(r.URL.Path, "/api"):
|
||||
next.ServeHTTP(w, r)
|
||||
default:
|
||||
buildFs, err := fs.Sub(portal_ui.GetStaticAssets(), "build")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
wrapHandlerSinglePageApplication(http.FileServer(http.FS(buildFs))).ServeHTTP(w, r)
|
||||
spaFileHandler.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -373,13 +468,15 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
|
||||
sf.HideMenu = true
|
||||
sf.ObjectBrowser = true
|
||||
|
||||
err := ValidateEncodedStyles(overridenStyles)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if overridenStyles != "" {
|
||||
err := ValidateEncodedStyles(overridenStyles)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
sf.CustomStyleOB = overridenStyles
|
||||
sf.CustomStyleOB = overridenStyles
|
||||
}
|
||||
|
||||
sessionID, err := login(consoleCreds, sf)
|
||||
if err != nil {
|
||||
@@ -408,14 +505,12 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
|
||||
} else if getSubPath() != "/" {
|
||||
indexPageBytes = replaceBaseInIndex(indexPageBytes, getSubPath())
|
||||
}
|
||||
indexPageBytes = replaceLicense(indexPageBytes)
|
||||
|
||||
mimeType := mimedb.TypeByExtension(filepath.Ext(r.URL.Path))
|
||||
|
||||
if mimeType == "application/octet-stream" {
|
||||
mimeType = "text/html"
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", mimeType)
|
||||
// it's important to force "Content-Type: text/html", because a previous
|
||||
// handler may have already set the content-type to a different value.
|
||||
// (i.e. the FileServer when it detected that it couldn't find the file)
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(indexPageBytes))
|
||||
}
|
||||
|
||||
@@ -453,9 +548,9 @@ func configureServer(s *http.Server, _, _ string) {
|
||||
|
||||
func getSubPath() string {
|
||||
subPathOnce.Do(func() {
|
||||
subPath = parseSubPath(env.Get(SubPath, ""))
|
||||
cfgSubPath = parseSubPath(env.Get(SubPath, ""))
|
||||
})
|
||||
return subPath
|
||||
return cfgSubPath
|
||||
}
|
||||
|
||||
func parseSubPath(v string) string {
|
||||
@@ -465,7 +560,7 @@ func parseSubPath(v string) string {
|
||||
}
|
||||
// Replace all unnecessary `\` to `/`
|
||||
// also add pro-actively at the end.
|
||||
subPath = path.Clean(filepath.ToSlash(v))
|
||||
subPath := path.Clean(filepath.ToSlash(v))
|
||||
if !strings.HasPrefix(subPath, SlashSeparator) {
|
||||
subPath = SlashSeparator + subPath
|
||||
}
|
||||
@@ -489,3 +584,20 @@ func replaceBaseInIndex(indexPageBytes []byte, basePath string) []byte {
|
||||
}
|
||||
return indexPageBytes
|
||||
}
|
||||
|
||||
func replaceLicense(indexPageBytes []byte) []byte {
|
||||
indexPageStr := string(indexPageBytes)
|
||||
indexPageBytes = []byte(indexPageStr)
|
||||
return indexPageBytes
|
||||
}
|
||||
|
||||
func requestBounce(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -70,7 +70,7 @@ func Test_parseSubPath(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
assert.Equalf(t, tt.want, parseSubPath(tt.args.v), "parseSubPath(%v)", tt.args.v)
|
||||
})
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func Test_getSubPath(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
t.Setenv(SubPath, tt.args.envValue)
|
||||
defer os.Unsetenv(SubPath)
|
||||
subPathOnce = sync.Once{}
|
||||
63
api/consts.go
Normal file
63
api/consts.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
// list of all console environment constants
|
||||
const (
|
||||
// Constants for common configuration
|
||||
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
|
||||
ConsoleSubnetProxy = "CONSOLE_SUBNET_PROXY"
|
||||
ConsoleMinIORegion = "CONSOLE_MINIO_REGION"
|
||||
ConsoleHostname = "CONSOLE_HOSTNAME"
|
||||
ConsolePort = "CONSOLE_PORT"
|
||||
ConsoleTLSPort = "CONSOLE_TLS_PORT"
|
||||
|
||||
// Constants for Secure middleware
|
||||
ConsoleSecureAllowedHosts = "CONSOLE_SECURE_ALLOWED_HOSTS"
|
||||
ConsoleSecureAllowedHostsAreRegex = "CONSOLE_SECURE_ALLOWED_HOSTS_ARE_REGEX"
|
||||
ConsoleSecureFrameDeny = "CONSOLE_SECURE_FRAME_DENY"
|
||||
ConsoleSecureContentTypeNoSniff = "CONSOLE_SECURE_CONTENT_TYPE_NO_SNIFF"
|
||||
ConsoleSecureBrowserXSSFilter = "CONSOLE_SECURE_BROWSER_XSS_FILTER"
|
||||
ConsoleSecureContentSecurityPolicy = "CONSOLE_SECURE_CONTENT_SECURITY_POLICY"
|
||||
ConsoleSecureContentSecurityPolicyReportOnly = "CONSOLE_SECURE_CONTENT_SECURITY_POLICY_REPORT_ONLY"
|
||||
ConsoleSecureHostsProxyHeaders = "CONSOLE_SECURE_HOSTS_PROXY_HEADERS"
|
||||
ConsoleSecureSTSSeconds = "CONSOLE_SECURE_STS_SECONDS"
|
||||
ConsoleSecureSTSIncludeSubdomains = "CONSOLE_SECURE_STS_INCLUDE_SUB_DOMAINS"
|
||||
ConsoleSecureSTSPreload = "CONSOLE_SECURE_STS_PRELOAD"
|
||||
ConsoleSecureTLSRedirect = "CONSOLE_SECURE_TLS_REDIRECT"
|
||||
ConsoleSecureTLSHost = "CONSOLE_SECURE_TLS_HOST"
|
||||
ConsoleSecureTLSTemporaryRedirect = "CONSOLE_SECURE_TLS_TEMPORARY_REDIRECT"
|
||||
ConsoleSecureForceSTSHeader = "CONSOLE_SECURE_FORCE_STS_HEADER"
|
||||
ConsoleSecurePublicKey = "CONSOLE_SECURE_PUBLIC_KEY"
|
||||
ConsoleSecureReferrerPolicy = "CONSOLE_SECURE_REFERRER_POLICY"
|
||||
ConsoleSecureFeaturePolicy = "CONSOLE_SECURE_FEATURE_POLICY"
|
||||
ConsoleSecureExpectCTHeader = "CONSOLE_SECURE_EXPECT_CT_HEADER"
|
||||
PrometheusURL = "CONSOLE_PROMETHEUS_URL"
|
||||
PrometheusAuthToken = "CONSOLE_PROMETHEUS_AUTH_TOKEN"
|
||||
PrometheusJobID = "CONSOLE_PROMETHEUS_JOB_ID"
|
||||
PrometheusExtraLabels = "CONSOLE_PROMETHEUS_EXTRA_LABELS"
|
||||
ConsoleLogQueryURL = "CONSOLE_LOG_QUERY_URL"
|
||||
ConsoleLogQueryAuthToken = "CONSOLE_LOG_QUERY_AUTH_TOKEN"
|
||||
ConsoleMaxConcurrentUploads = "CONSOLE_MAX_CONCURRENT_UPLOADS"
|
||||
ConsoleMaxConcurrentDownloads = "CONSOLE_MAX_CONCURRENT_DOWNLOADS"
|
||||
ConsoleDevMode = "CONSOLE_DEV_MODE"
|
||||
ConsoleAnimatedLogin = "CONSOLE_ANIMATED_LOGIN"
|
||||
ConsoleBrowserRedirectURL = "CONSOLE_BROWSER_REDIRECT_URL"
|
||||
LogSearchQueryAuthToken = "LOGSEARCH_QUERY_AUTH_TOKEN"
|
||||
SlashSeparator = "/"
|
||||
LocalAddress = "127.0.0.1"
|
||||
)
|
||||
556
api/custom-server.go
Normal file
556
api/custom-server.go
Normal file
@@ -0,0 +1,556 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/flagext"
|
||||
"github.com/go-openapi/swag"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
"golang.org/x/net/netutil"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
)
|
||||
|
||||
const (
|
||||
schemeHTTP = "http"
|
||||
schemeHTTPS = "https"
|
||||
schemeUnix = "unix"
|
||||
)
|
||||
|
||||
var defaultSchemes []string
|
||||
|
||||
func init() {
|
||||
defaultSchemes = []string{
|
||||
schemeHTTP,
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer creates a new api console server but does not configure it
|
||||
func NewServer(api *operations.ConsoleAPI) *Server {
|
||||
s := new(Server)
|
||||
|
||||
s.shutdown = make(chan struct{})
|
||||
s.api = api
|
||||
s.interrupt = make(chan os.Signal, 1)
|
||||
return s
|
||||
}
|
||||
|
||||
// ConfigureAPI configures the API and handlers.
|
||||
func (s *Server) ConfigureAPI() {
|
||||
if s.api != nil {
|
||||
s.handler = configureAPI(s.api)
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
|
||||
func (s *Server) ConfigureFlags() {
|
||||
if s.api != nil {
|
||||
configureFlags(s.api)
|
||||
}
|
||||
}
|
||||
|
||||
// Server for the console API
|
||||
type Server struct {
|
||||
EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
|
||||
CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
|
||||
GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
|
||||
MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
|
||||
|
||||
SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/console.sock"`
|
||||
domainSocketL net.Listener
|
||||
|
||||
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
|
||||
Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
|
||||
ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
|
||||
KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
|
||||
ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
|
||||
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
|
||||
httpServerL []net.Listener
|
||||
|
||||
TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
|
||||
TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
|
||||
TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
|
||||
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
|
||||
TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
|
||||
TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
|
||||
TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
|
||||
TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
|
||||
TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
|
||||
httpsServerL []net.Listener
|
||||
|
||||
api *operations.ConsoleAPI
|
||||
handler http.Handler
|
||||
hasListeners bool
|
||||
shutdown chan struct{}
|
||||
shuttingDown int32
|
||||
interrupted bool
|
||||
interrupt chan os.Signal
|
||||
}
|
||||
|
||||
// Logf logs message either via defined user logger or via system one if no user logger is defined.
|
||||
func (s *Server) Logf(f string, args ...interface{}) {
|
||||
if s.api != nil && s.api.Logger != nil {
|
||||
s.api.Logger(f, args...)
|
||||
} else {
|
||||
log.Printf(f, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
|
||||
// Exits with non-zero status after printing
|
||||
func (s *Server) Fatalf(f string, args ...interface{}) {
|
||||
if s.api != nil && s.api.Logger != nil {
|
||||
s.api.Logger(f, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Fatalf(f, args...)
|
||||
}
|
||||
|
||||
// SetAPI configures the server with the specified API. Needs to be called before Serve
|
||||
func (s *Server) SetAPI(api *operations.ConsoleAPI) {
|
||||
if api == nil {
|
||||
s.api = nil
|
||||
s.handler = nil
|
||||
return
|
||||
}
|
||||
|
||||
s.api = api
|
||||
s.handler = configureAPI(api)
|
||||
}
|
||||
|
||||
func (s *Server) hasScheme(scheme string) bool {
|
||||
schemes := s.EnabledListeners
|
||||
if len(schemes) == 0 {
|
||||
schemes = defaultSchemes
|
||||
}
|
||||
|
||||
for _, v := range schemes {
|
||||
if v == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Serve the api
|
||||
func (s *Server) Serve() (err error) {
|
||||
if !s.hasListeners {
|
||||
if err = s.Listen(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set default handler, if none is set
|
||||
if s.handler == nil {
|
||||
if s.api == nil {
|
||||
return errors.New("can't create the default handler, as no api is set")
|
||||
}
|
||||
|
||||
s.SetHandler(s.api.Serve(nil))
|
||||
}
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
once := new(sync.Once)
|
||||
signalNotify(s.interrupt)
|
||||
go handleInterrupt(once, s)
|
||||
|
||||
servers := []*http.Server{}
|
||||
|
||||
if s.hasScheme(schemeUnix) {
|
||||
domainSocket := new(http.Server)
|
||||
domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
|
||||
domainSocket.Handler = s.handler
|
||||
if int64(s.CleanupTimeout) > 0 {
|
||||
domainSocket.IdleTimeout = s.CleanupTimeout
|
||||
}
|
||||
|
||||
configureServer(domainSocket, "unix", string(s.SocketPath))
|
||||
|
||||
servers = append(servers, domainSocket)
|
||||
wg.Add(1)
|
||||
s.Logf("Serving console at unix://%s", s.SocketPath)
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
}
|
||||
s.Logf("Stopped serving console at unix://%s", s.SocketPath)
|
||||
}(s.domainSocketL)
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeHTTP) {
|
||||
httpServer := new(http.Server)
|
||||
httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
|
||||
httpServer.ReadTimeout = s.ReadTimeout
|
||||
httpServer.WriteTimeout = s.WriteTimeout
|
||||
httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
|
||||
if s.ListenLimit > 0 {
|
||||
for i := range s.httpServerL {
|
||||
s.httpServerL[i] = netutil.LimitListener(s.httpServerL[i], s.ListenLimit)
|
||||
}
|
||||
}
|
||||
|
||||
if int64(s.CleanupTimeout) > 0 {
|
||||
httpServer.IdleTimeout = s.CleanupTimeout
|
||||
}
|
||||
|
||||
httpServer.Handler = s.handler
|
||||
|
||||
configureServer(httpServer, "http", s.httpServerL[0].Addr().String())
|
||||
|
||||
servers = append(servers, httpServer)
|
||||
s.Logf("Serving console at http://%s", s.httpServerL[0].Addr())
|
||||
for i := range s.httpServerL {
|
||||
wg.Add(1)
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
}
|
||||
s.Logf("Stopped serving console at http://%s", l.Addr())
|
||||
}(s.httpServerL[i])
|
||||
}
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeHTTPS) {
|
||||
httpsServer := new(http.Server)
|
||||
httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
|
||||
httpsServer.ReadTimeout = s.TLSReadTimeout
|
||||
httpsServer.WriteTimeout = s.TLSWriteTimeout
|
||||
httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
|
||||
if s.TLSListenLimit > 0 {
|
||||
for i := range s.httpsServerL {
|
||||
s.httpsServerL[i] = netutil.LimitListener(s.httpsServerL[i], s.TLSListenLimit)
|
||||
}
|
||||
}
|
||||
if int64(s.CleanupTimeout) > 0 {
|
||||
httpsServer.IdleTimeout = s.CleanupTimeout
|
||||
}
|
||||
httpsServer.Handler = s.handler
|
||||
|
||||
// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
|
||||
httpsServer.TLSConfig = &tls.Config{
|
||||
// Causes servers to use Go's default ciphersuite preferences,
|
||||
// which are tuned to avoid attacks. Does nothing on clients.
|
||||
PreferServerCipherSuites: true,
|
||||
// Only use curves which have assembly implementations
|
||||
// https://github.com/golang/go/tree/master/src/crypto/elliptic
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP256},
|
||||
// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
|
||||
MinVersion: tls.VersionTLS12,
|
||||
// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
},
|
||||
}
|
||||
|
||||
// build standard config from server options
|
||||
if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
|
||||
httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||
httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.TLSCACertificate != "" {
|
||||
// include specified CA certificate
|
||||
caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate))
|
||||
if caCertErr != nil {
|
||||
return caCertErr
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
ok := caCertPool.AppendCertsFromPEM(caCert)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot parse CA certificate")
|
||||
}
|
||||
httpsServer.TLSConfig.ClientCAs = caCertPool
|
||||
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
// call custom TLS configurator
|
||||
configureTLS(httpsServer.TLSConfig)
|
||||
|
||||
if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
|
||||
// after standard and custom config are passed, this ends up with no certificate
|
||||
if s.TLSCertificate == "" {
|
||||
if s.TLSCertificateKey == "" {
|
||||
s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
|
||||
}
|
||||
s.Fatalf("the required flag `--tls-certificate` was not specified")
|
||||
}
|
||||
if s.TLSCertificateKey == "" {
|
||||
s.Fatalf("the required flag `--tls-key` was not specified")
|
||||
}
|
||||
// this happens with a wrong custom TLS configurator
|
||||
s.Fatalf("no certificate was configured for TLS")
|
||||
}
|
||||
|
||||
configureServer(httpsServer, "https", s.httpsServerL[0].Addr().String())
|
||||
|
||||
servers = append(servers, httpsServer)
|
||||
s.Logf("Serving console at https://%s", s.httpsServerL[0].Addr())
|
||||
for i := range s.httpsServerL {
|
||||
wg.Add(1)
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
|
||||
s.Fatalf("%v", err)
|
||||
}
|
||||
s.Logf("Stopped serving console at https://%s", l.Addr())
|
||||
}(tls.NewListener(s.httpsServerL[i], httpsServer.TLSConfig))
|
||||
}
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go s.handleShutdown(wg, &servers)
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen creates the listeners for the server
|
||||
func (s *Server) Listen() error {
|
||||
if s.hasListeners { // already done this
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeHTTPS) {
|
||||
// Use http host if https host wasn't defined
|
||||
if s.TLSHost == "" {
|
||||
s.TLSHost = s.Host
|
||||
}
|
||||
// Use http listen limit if https listen limit wasn't defined
|
||||
if s.TLSListenLimit == 0 {
|
||||
s.TLSListenLimit = s.ListenLimit
|
||||
}
|
||||
// Use http tcp keep alive if https tcp keep alive wasn't defined
|
||||
if int64(s.TLSKeepAlive) == 0 {
|
||||
s.TLSKeepAlive = s.KeepAlive
|
||||
}
|
||||
// Use http read timeout if https read timeout wasn't defined
|
||||
if int64(s.TLSReadTimeout) == 0 {
|
||||
s.TLSReadTimeout = s.ReadTimeout
|
||||
}
|
||||
// Use http write timeout if https write timeout wasn't defined
|
||||
if int64(s.TLSWriteTimeout) == 0 {
|
||||
s.TLSWriteTimeout = s.WriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeUnix) {
|
||||
domSockListener, err := net.Listen("unix", string(s.SocketPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.domainSocketL = domSockListener
|
||||
}
|
||||
|
||||
lookup := func(addr string) []net.IP {
|
||||
ips, err := net.LookupIP(addr)
|
||||
if err == nil {
|
||||
return ips
|
||||
}
|
||||
return []net.IP{net.ParseIP(addr)}
|
||||
}
|
||||
|
||||
convert := func(ip net.IP) (string, string) {
|
||||
if ip == nil {
|
||||
return "", "tcp"
|
||||
}
|
||||
proto := "tcp4"
|
||||
if ip.To4() == nil {
|
||||
proto = "tcp6"
|
||||
}
|
||||
return ip.String(), proto
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeHTTP) {
|
||||
for _, ip := range lookup(s.Host) {
|
||||
host, proto := convert(ip)
|
||||
listener, err := net.Listen(proto, net.JoinHostPort(host, strconv.Itoa(s.Port)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Host == "" || s.Port == 0 {
|
||||
h, p, err := swag.SplitHostPort(listener.Addr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Host = h
|
||||
s.Port = p
|
||||
}
|
||||
s.httpServerL = append(s.httpServerL, listener)
|
||||
}
|
||||
}
|
||||
|
||||
if s.hasScheme(schemeHTTPS) {
|
||||
for _, ip := range lookup(s.TLSHost) {
|
||||
host, proto := convert(ip)
|
||||
tlsListener, err := net.Listen(proto, net.JoinHostPort(host, strconv.Itoa(s.TLSPort)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.TLSHost == "" || s.TLSPort == 0 {
|
||||
sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.TLSHost = sh
|
||||
s.TLSPort = sp
|
||||
}
|
||||
s.httpsServerL = append(s.httpsServerL, tlsListener)
|
||||
}
|
||||
}
|
||||
|
||||
s.hasListeners = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown server and clean up resources
|
||||
func (s *Server) Shutdown() error {
|
||||
if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
|
||||
close(s.shutdown)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
|
||||
// wg.Done must occur last, after s.api.ServerShutdown()
|
||||
// (to preserve old behavior)
|
||||
defer wg.Done()
|
||||
|
||||
<-s.shutdown
|
||||
|
||||
servers := *serversPtr
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
|
||||
defer cancel()
|
||||
|
||||
// first execute the pre-shutdown hook
|
||||
s.api.PreServerShutdown()
|
||||
|
||||
shutdownChan := make(chan bool)
|
||||
for i := range servers {
|
||||
server := servers[i]
|
||||
go func() {
|
||||
var success bool
|
||||
defer func() {
|
||||
shutdownChan <- success
|
||||
}()
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
// Error from closing listeners, or context timeout:
|
||||
s.Logf("HTTP server Shutdown: %v", err)
|
||||
} else {
|
||||
success = true
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait until all listeners have successfully shut down before calling ServerShutdown
|
||||
success := true
|
||||
for range servers {
|
||||
success = success && <-shutdownChan
|
||||
}
|
||||
if success {
|
||||
s.api.ServerShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// GetHandler returns a handler useful for testing
|
||||
func (s *Server) GetHandler() http.Handler {
|
||||
return s.handler
|
||||
}
|
||||
|
||||
// SetHandler allows for setting a http handler on this server
|
||||
func (s *Server) SetHandler(handler http.Handler) {
|
||||
s.handler = handler
|
||||
}
|
||||
|
||||
// UnixListener returns the domain socket listener
|
||||
func (s *Server) UnixListener() (net.Listener, error) {
|
||||
if !s.hasListeners {
|
||||
if err := s.Listen(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return s.domainSocketL, nil
|
||||
}
|
||||
|
||||
// HTTPListener returns the http listener
|
||||
func (s *Server) HTTPListener() ([]net.Listener, error) {
|
||||
if !s.hasListeners {
|
||||
if err := s.Listen(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return s.httpServerL, nil
|
||||
}
|
||||
|
||||
// TLSListener returns the https listener
|
||||
func (s *Server) TLSListener() ([]net.Listener, error) {
|
||||
if !s.hasListeners {
|
||||
if err := s.Listen(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return s.httpsServerL, nil
|
||||
}
|
||||
|
||||
func handleInterrupt(once *sync.Once, s *Server) {
|
||||
once.Do(func() {
|
||||
for range s.interrupt {
|
||||
if s.interrupted {
|
||||
s.Logf("Server already shutting down")
|
||||
continue
|
||||
}
|
||||
s.interrupted = true
|
||||
s.Logf("Shutting down... ")
|
||||
if err := s.Shutdown(); err != nil {
|
||||
s.Logf("HTTP server Shutdown: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func signalNotify(interrupt chan<- os.Signal) {
|
||||
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
}
|
||||
37
api/doc.go
Normal file
37
api/doc.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Package api MinIO Console Server
|
||||
//
|
||||
// Schemes:
|
||||
// http
|
||||
// ws
|
||||
// Host: localhost
|
||||
// BasePath: /api/v1
|
||||
// Version: 0.1.0
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
// - multipart/form-data
|
||||
//
|
||||
// Produces:
|
||||
// - application/octet-stream
|
||||
// - application/json
|
||||
//
|
||||
// swagger:meta
|
||||
package api
|
||||
14715
api/embedded_spec.go
Normal file
14715
api/embedded_spec.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,22 +14,22 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/minio-go/v7"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDefault = errors.New("an error occurred, please try again")
|
||||
ErrInvalidLogin = errors.New("invalid Login")
|
||||
ErrInvalidLogin = errors.New("invalid login")
|
||||
ErrForbidden = errors.New("403 Forbidden")
|
||||
ErrBadRequest = errors.New("400 Bad Request")
|
||||
ErrFileTooLarge = errors.New("413 File too Large")
|
||||
@@ -43,7 +43,6 @@ var (
|
||||
ErrGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
ErrPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
ErrPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
ErrPolicyNameContainsSpace = errors.New("error policy name cannot contain spaces")
|
||||
ErrInvalidEncryptionAlgorithm = errors.New("error invalid encryption algorithm")
|
||||
ErrSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
ErrBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
@@ -53,6 +52,7 @@ var (
|
||||
ErrAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
|
||||
ErrAccessDenied = errors.New("access denied")
|
||||
ErrOauth2Provider = errors.New("unable to contact configured identity provider")
|
||||
ErrOauth2Login = errors.New("unable to login using configured identity provider")
|
||||
ErrNonUniqueAccessKey = errors.New("access key already in use")
|
||||
ErrRemoteTierExists = errors.New("specified remote tier already exists")
|
||||
ErrRemoteTierNotFound = errors.New("specified remote tier was not found")
|
||||
@@ -71,16 +71,25 @@ var (
|
||||
ErrEncryptionConfigNotFound = errors.New("encryption configuration not found")
|
||||
ErrPolicyNotFound = errors.New("policy does not exist")
|
||||
ErrLoginNotAllowed = errors.New("login not allowed")
|
||||
ErrHealthReportFail = errors.New("failure to generate Health report")
|
||||
ErrNetworkError = errors.New("unable to login due to network error")
|
||||
)
|
||||
|
||||
type CodedAPIError struct {
|
||||
Code int
|
||||
APIError *models.APIError
|
||||
}
|
||||
|
||||
// ErrorWithContext :
|
||||
func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorCode := int32(500)
|
||||
func ErrorWithContext(ctx context.Context, err ...interface{}) *CodedAPIError {
|
||||
errorCode := 500
|
||||
errorMessage := ErrDefault.Error()
|
||||
var detailedMessage string
|
||||
var err1 error
|
||||
var exists bool
|
||||
if len(err) > 0 {
|
||||
if err1, exists = err[0].(error); exists {
|
||||
detailedMessage = err1.Error()
|
||||
var lastError error
|
||||
if len(err) > 1 {
|
||||
if err2, lastExists := err[1].(error); lastExists {
|
||||
@@ -98,15 +107,27 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorMessage = ErrNotFound.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrInvalidLogin) {
|
||||
detailedMessage = ""
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidLogin.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrNetworkError) {
|
||||
detailedMessage = ""
|
||||
errorCode = 503
|
||||
errorMessage = ErrNetworkError.Error()
|
||||
}
|
||||
if strings.Contains(strings.ToLower(err1.Error()), ErrAccessDenied.Error()) {
|
||||
errorCode = 403
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
// If the last error is ErrInvalidLogin, this is a login failure
|
||||
if errors.Is(lastError, ErrInvalidLogin) {
|
||||
detailedMessage = ""
|
||||
errorCode = 401
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
if strings.Contains(err1.Error(), ErrLoginNotAllowed.Error()) {
|
||||
detailedMessage = ""
|
||||
errorCode = 400
|
||||
errorMessage = ErrLoginNotAllowed.Error()
|
||||
}
|
||||
@@ -139,10 +160,6 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrPolicyNameContainsSpace) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyNameContainsSpace.Error()
|
||||
}
|
||||
// console invalid session errors
|
||||
if errors.Is(err1, ErrInvalidSession) {
|
||||
errorCode = 401
|
||||
@@ -200,6 +217,7 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorMessage = ErrAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err1).Code == "InvalidAccessKeyId" {
|
||||
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidSession.Error()
|
||||
}
|
||||
@@ -239,6 +257,7 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorCode = 400
|
||||
errorMessage = "Bucket already exists"
|
||||
}
|
||||
|
||||
LogError("ErrorWithContext:%v", err...)
|
||||
LogIf(ctx, err1, err...)
|
||||
}
|
||||
@@ -249,11 +268,11 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err1.Error())}
|
||||
return &CodedAPIError{Code: errorCode, APIError: &models.APIError{Message: errorMessage, DetailedMessage: detailedMessage}}
|
||||
}
|
||||
|
||||
// Error receives an errors object and parse it against k8sErrors, returns the right errors code paired with a generic errors message
|
||||
func Error(err ...interface{}) *models.Error {
|
||||
func Error(err ...interface{}) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
return ErrorWithContext(ctx, err...)
|
||||
158
api/errors_test.go
Normal file
158
api/errors_test.go
Normal file
@@ -0,0 +1,158 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
type args struct {
|
||||
err []interface{}
|
||||
}
|
||||
|
||||
type testError struct {
|
||||
name string
|
||||
args args
|
||||
want *CodedAPIError
|
||||
}
|
||||
|
||||
var tests []testError
|
||||
|
||||
type expectedError struct {
|
||||
err error
|
||||
code int
|
||||
}
|
||||
|
||||
appErrors := map[string]expectedError{
|
||||
"ErrDefault": {code: 500, err: ErrDefault},
|
||||
|
||||
"ErrForbidden": {code: 403, err: ErrForbidden},
|
||||
"ErrFileTooLarge": {code: 413, err: ErrFileTooLarge},
|
||||
"ErrInvalidSession": {code: 401, err: ErrInvalidSession},
|
||||
"ErrNotFound": {code: 404, err: ErrNotFound},
|
||||
"ErrGroupAlreadyExists": {code: 400, err: ErrGroupAlreadyExists},
|
||||
"ErrInvalidErasureCodingValue": {code: 400, err: ErrInvalidErasureCodingValue},
|
||||
"ErrBucketBodyNotInRequest": {code: 400, err: ErrBucketBodyNotInRequest},
|
||||
"ErrBucketNameNotInRequest": {code: 400, err: ErrBucketNameNotInRequest},
|
||||
"ErrGroupBodyNotInRequest": {code: 400, err: ErrGroupBodyNotInRequest},
|
||||
"ErrGroupNameNotInRequest": {code: 400, err: ErrGroupNameNotInRequest},
|
||||
"ErrPolicyNameNotInRequest": {code: 400, err: ErrPolicyNameNotInRequest},
|
||||
"ErrPolicyBodyNotInRequest": {code: 400, err: ErrPolicyBodyNotInRequest},
|
||||
"ErrInvalidEncryptionAlgorithm": {code: 500, err: ErrInvalidEncryptionAlgorithm},
|
||||
"ErrSSENotConfigured": {code: 404, err: ErrSSENotConfigured},
|
||||
"ErrChangePassword": {code: 403, err: ErrChangePassword},
|
||||
"ErrInvalidLicense": {code: 404, err: ErrInvalidLicense},
|
||||
"ErrLicenseNotFound": {code: 404, err: ErrLicenseNotFound},
|
||||
"ErrAvoidSelfAccountDelete": {code: 403, err: ErrAvoidSelfAccountDelete},
|
||||
|
||||
"ErrNonUniqueAccessKey": {code: 500, err: ErrNonUniqueAccessKey},
|
||||
"ErrRemoteTierExists": {code: 400, err: ErrRemoteTierExists},
|
||||
"ErrRemoteTierNotFound": {code: 400, err: ErrRemoteTierNotFound},
|
||||
"ErrRemoteTierUppercase": {code: 400, err: ErrRemoteTierUppercase},
|
||||
"ErrRemoteTierBucketNotFound": {code: 400, err: ErrRemoteTierBucketNotFound},
|
||||
"ErrRemoteInvalidCredentials": {code: 403, err: ErrRemoteInvalidCredentials},
|
||||
"ErrTooFewNodes": {code: 500, err: ErrTooFewNodes},
|
||||
"ErrUnableToGetTenantUsage": {code: 500, err: ErrUnableToGetTenantUsage},
|
||||
"ErrTooManyNodes": {code: 500, err: ErrTooManyNodes},
|
||||
"ErrAccessDenied": {code: 403, err: ErrAccessDenied},
|
||||
"ErrTooFewAvailableNodes": {code: 500, err: ErrTooFewAvailableNodes},
|
||||
"ErrFewerThanFourNodes": {code: 500, err: ErrFewerThanFourNodes},
|
||||
"ErrUnableToGetTenantLogs": {code: 500, err: ErrUnableToGetTenantLogs},
|
||||
"ErrUnableToUpdateTenantCertificates": {code: 500, err: ErrUnableToUpdateTenantCertificates},
|
||||
"ErrUpdatingEncryptionConfig": {code: 500, err: ErrUpdatingEncryptionConfig},
|
||||
"ErrDeletingEncryptionConfig": {code: 500, err: ErrDeletingEncryptionConfig},
|
||||
"ErrEncryptionConfigNotFound": {code: 404, err: ErrEncryptionConfigNotFound},
|
||||
}
|
||||
|
||||
for k, e := range appErrors {
|
||||
tests = append(tests, testError{
|
||||
name: fmt.Sprintf("%s error", k),
|
||||
args: args{
|
||||
err: []interface{}{e.err},
|
||||
},
|
||||
want: &CodedAPIError{
|
||||
Code: e.code,
|
||||
APIError: &models.APIError{Message: e.err.Error(), DetailedMessage: e.err.Error()},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
tests = append(tests,
|
||||
testError{
|
||||
name: "passing multiple errors but ErrInvalidLogin is last",
|
||||
args: args{
|
||||
err: []interface{}{ErrDefault, ErrInvalidLogin},
|
||||
},
|
||||
want: &CodedAPIError{
|
||||
Code: int(401),
|
||||
APIError: &models.APIError{Message: ErrDefault.Error(), DetailedMessage: ""},
|
||||
},
|
||||
})
|
||||
tests = append(tests,
|
||||
testError{
|
||||
name: "login error omits detailedMessage",
|
||||
args: args{
|
||||
err: []interface{}{ErrInvalidLogin},
|
||||
},
|
||||
want: &CodedAPIError{
|
||||
Code: int(401),
|
||||
APIError: &models.APIError{Message: ErrInvalidLogin.Error(), DetailedMessage: ""},
|
||||
},
|
||||
})
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
got := Error(tt.args.err...)
|
||||
assert.Equalf(t, tt.want.Code, got.Code, "Error(%v) Got (%v)", tt.want.Code, got.Code)
|
||||
assert.Equalf(t, tt.want.APIError.DetailedMessage, got.APIError.DetailedMessage, "Error(%s) Got (%s)", tt.want.APIError.DetailedMessage, got.APIError.DetailedMessage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWithContext(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
err []interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *CodedAPIError
|
||||
}{
|
||||
{
|
||||
name: "default error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
err: []interface{}{ErrDefault},
|
||||
},
|
||||
want: &CodedAPIError{
|
||||
Code: 500, APIError: &models.APIError{Message: ErrDefault.Error(), DetailedMessage: ErrDefault.Error()},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
assert.Equalf(t, tt.want, ErrorWithContext(tt.args.ctx, tt.args.err...), "ErrorWithContext(%v, %v)", tt.args.ctx, tt.args.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
83
api/logs.go
Normal file
83
api/logs.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/minio/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
infoLog = log.New(os.Stdout, "I: ", log.LstdFlags)
|
||||
errorLog = log.New(os.Stdout, "E: ", log.LstdFlags)
|
||||
)
|
||||
|
||||
func logInfo(msg string, data ...interface{}) {
|
||||
infoLog.Printf(msg+"\n", data...)
|
||||
}
|
||||
|
||||
func logError(msg string, data ...interface{}) {
|
||||
errorLog.Printf(msg+"\n", data...)
|
||||
}
|
||||
|
||||
func logIf(_ context.Context, _ error, _ ...interface{}) {
|
||||
}
|
||||
|
||||
// globally changeable logger styles
|
||||
var (
|
||||
LogInfo = logInfo
|
||||
LogError = logError
|
||||
LogIf = logIf
|
||||
)
|
||||
|
||||
// Context captures all command line flags values
|
||||
type Context struct {
|
||||
Host string
|
||||
HTTPPort, HTTPSPort int
|
||||
TLSRedirect string
|
||||
// Legacy options, TODO: remove in future
|
||||
TLSCertificate, TLSKey, TLSca string
|
||||
}
|
||||
|
||||
// Load loads api Context from command line context.
|
||||
func (c *Context) Load(ctx *cli.Context) error {
|
||||
*c = Context{
|
||||
Host: ctx.String("host"),
|
||||
HTTPPort: ctx.Int("port"),
|
||||
HTTPSPort: ctx.Int("tls-port"),
|
||||
TLSRedirect: ctx.String("tls-redirect"),
|
||||
// Legacy options to be removed.
|
||||
TLSCertificate: ctx.String("tls-certificate"),
|
||||
TLSKey: ctx.String("tls-key"),
|
||||
TLSca: ctx.String("tls-ca"),
|
||||
}
|
||||
if c.HTTPPort > 65535 {
|
||||
return errors.New("invalid argument --port out of range - ports can range from 1-65535")
|
||||
}
|
||||
if c.HTTPSPort > 65535 {
|
||||
return errors.New("invalid argument --tls-port out of range - ports can range from 1-65535")
|
||||
}
|
||||
if c.TLSRedirect != "on" && c.TLSRedirect != "off" {
|
||||
return errors.New("invalid argument --tls-redirect only accepts either 'on' or 'off'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
110
api/logs_test.go
Normal file
110
api/logs_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContext_Load(t *testing.T) {
|
||||
type fields struct {
|
||||
Host string
|
||||
HTTPPort int
|
||||
HTTPSPort int
|
||||
TLSRedirect string
|
||||
TLSCertificate string
|
||||
TLSKey string
|
||||
TLSca string
|
||||
}
|
||||
type args struct {
|
||||
values map[string]string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid args",
|
||||
args: args{
|
||||
values: map[string]string{
|
||||
"tls-redirect": "on",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid args",
|
||||
args: args{
|
||||
values: map[string]string{
|
||||
"tls-redirect": "aaaa",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port http",
|
||||
args: args{
|
||||
values: map[string]string{
|
||||
"tls-redirect": "on",
|
||||
"port": "65536",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port https",
|
||||
args: args{
|
||||
values: map[string]string{
|
||||
"tls-redirect": "on",
|
||||
"port": "65534",
|
||||
"tls-port": "65536",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
c := &Context{}
|
||||
|
||||
fs := flag.NewFlagSet("flags", flag.ContinueOnError)
|
||||
for k, v := range tt.args.values {
|
||||
fs.String(k, v, "ok")
|
||||
}
|
||||
|
||||
ctx := cli.NewContext(nil, fs, &cli.Context{})
|
||||
|
||||
err := c.Load(ctx)
|
||||
if tt.wantErr {
|
||||
assert.NotNilf(t, err, fmt.Sprintf("Load(%v)", err))
|
||||
} else {
|
||||
assert.Nilf(t, err, fmt.Sprintf("Load(%v)", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_logInfo(_ *testing.T) {
|
||||
logInfo("message", nil)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -66,7 +66,7 @@ type AccountChangePasswordDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAccountChangePasswordDefault creates AccountChangePasswordDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *AccountChangePasswordDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the account change password default response
|
||||
func (o *AccountChangePasswordDefault) WithPayload(payload *models.Error) *AccountChangePasswordDefault {
|
||||
func (o *AccountChangePasswordDefault) WithPayload(payload *models.APIError) *AccountChangePasswordDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the account change password default response
|
||||
func (o *AccountChangePasswordDefault) SetPayload(payload *models.Error) {
|
||||
func (o *AccountChangePasswordDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -66,7 +66,7 @@ type ChangeUserPasswordDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewChangeUserPasswordDefault creates ChangeUserPasswordDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *ChangeUserPasswordDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the change user password default response
|
||||
func (o *ChangeUserPasswordDefault) WithPayload(payload *models.Error) *ChangeUserPasswordDefault {
|
||||
func (o *ChangeUserPasswordDefault) WithPayload(payload *models.APIError) *ChangeUserPasswordDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the change user password default response
|
||||
func (o *ChangeUserPasswordDefault) SetPayload(payload *models.Error) {
|
||||
func (o *ChangeUserPasswordDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
73
api/operations/auth/login.go
Normal file
73
api/operations/auth/login.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// LoginHandlerFunc turns a function with the right signature into a login handler
|
||||
type LoginHandlerFunc func(LoginParams) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LoginHandlerFunc) Handle(params LoginParams) middleware.Responder {
|
||||
return fn(params)
|
||||
}
|
||||
|
||||
// LoginHandler interface for that can handle valid login params
|
||||
type LoginHandler interface {
|
||||
Handle(LoginParams) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLogin creates a new http.Handler for the login operation
|
||||
func NewLogin(ctx *middleware.Context, handler LoginHandler) *Login {
|
||||
return &Login{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
Login swagger:route POST /login Auth login
|
||||
|
||||
Login to Console
|
||||
*/
|
||||
type Login struct {
|
||||
Context *middleware.Context
|
||||
Handler LoginHandler
|
||||
}
|
||||
|
||||
func (o *Login) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewLoginParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
73
api/operations/auth/login_detail.go
Normal file
73
api/operations/auth/login_detail.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// LoginDetailHandlerFunc turns a function with the right signature into a login detail handler
|
||||
type LoginDetailHandlerFunc func(LoginDetailParams) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LoginDetailHandlerFunc) Handle(params LoginDetailParams) middleware.Responder {
|
||||
return fn(params)
|
||||
}
|
||||
|
||||
// LoginDetailHandler interface for that can handle valid login detail params
|
||||
type LoginDetailHandler interface {
|
||||
Handle(LoginDetailParams) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLoginDetail creates a new http.Handler for the login detail operation
|
||||
func NewLoginDetail(ctx *middleware.Context, handler LoginDetailHandler) *LoginDetail {
|
||||
return &LoginDetail{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
LoginDetail swagger:route GET /login Auth loginDetail
|
||||
|
||||
Returns login strategy, form or sso.
|
||||
*/
|
||||
type LoginDetail struct {
|
||||
Context *middleware.Context
|
||||
Handler LoginDetailHandler
|
||||
}
|
||||
|
||||
func (o *LoginDetail) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewLoginDetailParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
63
api/operations/auth/login_detail_parameters.go
Normal file
63
api/operations/auth/login_detail_parameters.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// NewLoginDetailParams creates a new LoginDetailParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewLoginDetailParams() LoginDetailParams {
|
||||
|
||||
return LoginDetailParams{}
|
||||
}
|
||||
|
||||
// LoginDetailParams contains all the bound params for the login detail operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters LoginDetail
|
||||
type LoginDetailParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewLoginDetailParams() beforehand.
|
||||
func (o *LoginDetailParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
135
api/operations/auth/login_detail_responses.go
Normal file
135
api/operations/auth/login_detail_responses.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LoginDetailOKCode is the HTTP code returned for type LoginDetailOK
|
||||
const LoginDetailOKCode int = 200
|
||||
|
||||
/*
|
||||
LoginDetailOK A successful response.
|
||||
|
||||
swagger:response loginDetailOK
|
||||
*/
|
||||
type LoginDetailOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.LoginDetails `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginDetailOK creates LoginDetailOK with default headers values
|
||||
func NewLoginDetailOK() *LoginDetailOK {
|
||||
|
||||
return &LoginDetailOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login detail o k response
|
||||
func (o *LoginDetailOK) WithPayload(payload *models.LoginDetails) *LoginDetailOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login detail o k response
|
||||
func (o *LoginDetailOK) SetPayload(payload *models.LoginDetails) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginDetailOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
LoginDetailDefault Generic error response.
|
||||
|
||||
swagger:response loginDetailDefault
|
||||
*/
|
||||
type LoginDetailDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginDetailDefault creates LoginDetailDefault with default headers values
|
||||
func NewLoginDetailDefault(code int) *LoginDetailDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &LoginDetailDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the login detail default response
|
||||
func (o *LoginDetailDefault) WithStatusCode(code int) *LoginDetailDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the login detail default response
|
||||
func (o *LoginDetailDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login detail default response
|
||||
func (o *LoginDetailDefault) WithPayload(payload *models.APIError) *LoginDetailDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login detail default response
|
||||
func (o *LoginDetailDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginDetailDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
api/operations/auth/login_detail_urlbuilder.go
Normal file
104
api/operations/auth/login_detail_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// LoginDetailURL generates an URL for the login detail operation
|
||||
type LoginDetailURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginDetailURL) WithBasePath(bp string) *LoginDetailURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginDetailURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *LoginDetailURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/login"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *LoginDetailURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *LoginDetailURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *LoginDetailURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on LoginDetailURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on LoginDetailURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *LoginDetailURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
73
api/operations/auth/login_oauth2_auth.go
Normal file
73
api/operations/auth/login_oauth2_auth.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// LoginOauth2AuthHandlerFunc turns a function with the right signature into a login oauth2 auth handler
|
||||
type LoginOauth2AuthHandlerFunc func(LoginOauth2AuthParams) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LoginOauth2AuthHandlerFunc) Handle(params LoginOauth2AuthParams) middleware.Responder {
|
||||
return fn(params)
|
||||
}
|
||||
|
||||
// LoginOauth2AuthHandler interface for that can handle valid login oauth2 auth params
|
||||
type LoginOauth2AuthHandler interface {
|
||||
Handle(LoginOauth2AuthParams) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLoginOauth2Auth creates a new http.Handler for the login oauth2 auth operation
|
||||
func NewLoginOauth2Auth(ctx *middleware.Context, handler LoginOauth2AuthHandler) *LoginOauth2Auth {
|
||||
return &LoginOauth2Auth{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
LoginOauth2Auth swagger:route POST /login/oauth2/auth Auth loginOauth2Auth
|
||||
|
||||
Identity Provider oauth2 callback endpoint.
|
||||
*/
|
||||
type LoginOauth2Auth struct {
|
||||
Context *middleware.Context
|
||||
Handler LoginOauth2AuthHandler
|
||||
}
|
||||
|
||||
func (o *LoginOauth2Auth) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewLoginOauth2AuthParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
101
api/operations/auth/login_oauth2_auth_parameters.go
Normal file
101
api/operations/auth/login_oauth2_auth_parameters.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewLoginOauth2AuthParams creates a new LoginOauth2AuthParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewLoginOauth2AuthParams() LoginOauth2AuthParams {
|
||||
|
||||
return LoginOauth2AuthParams{}
|
||||
}
|
||||
|
||||
// LoginOauth2AuthParams contains all the bound params for the login oauth2 auth operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters LoginOauth2Auth
|
||||
type LoginOauth2AuthParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.LoginOauth2AuthRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewLoginOauth2AuthParams() beforehand.
|
||||
func (o *LoginOauth2AuthParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.LoginOauth2AuthRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
115
api/operations/auth/login_oauth2_auth_responses.go
Normal file
115
api/operations/auth/login_oauth2_auth_responses.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LoginOauth2AuthNoContentCode is the HTTP code returned for type LoginOauth2AuthNoContent
|
||||
const LoginOauth2AuthNoContentCode int = 204
|
||||
|
||||
/*
|
||||
LoginOauth2AuthNoContent A successful login.
|
||||
|
||||
swagger:response loginOauth2AuthNoContent
|
||||
*/
|
||||
type LoginOauth2AuthNoContent struct {
|
||||
}
|
||||
|
||||
// NewLoginOauth2AuthNoContent creates LoginOauth2AuthNoContent with default headers values
|
||||
func NewLoginOauth2AuthNoContent() *LoginOauth2AuthNoContent {
|
||||
|
||||
return &LoginOauth2AuthNoContent{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginOauth2AuthNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(204)
|
||||
}
|
||||
|
||||
/*
|
||||
LoginOauth2AuthDefault Generic error response.
|
||||
|
||||
swagger:response loginOauth2AuthDefault
|
||||
*/
|
||||
type LoginOauth2AuthDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginOauth2AuthDefault creates LoginOauth2AuthDefault with default headers values
|
||||
func NewLoginOauth2AuthDefault(code int) *LoginOauth2AuthDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &LoginOauth2AuthDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the login oauth2 auth default response
|
||||
func (o *LoginOauth2AuthDefault) WithStatusCode(code int) *LoginOauth2AuthDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the login oauth2 auth default response
|
||||
func (o *LoginOauth2AuthDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login oauth2 auth default response
|
||||
func (o *LoginOauth2AuthDefault) WithPayload(payload *models.APIError) *LoginOauth2AuthDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login oauth2 auth default response
|
||||
func (o *LoginOauth2AuthDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginOauth2AuthDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
api/operations/auth/login_oauth2_auth_urlbuilder.go
Normal file
104
api/operations/auth/login_oauth2_auth_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// LoginOauth2AuthURL generates an URL for the login oauth2 auth operation
|
||||
type LoginOauth2AuthURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginOauth2AuthURL) WithBasePath(bp string) *LoginOauth2AuthURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginOauth2AuthURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *LoginOauth2AuthURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/login/oauth2/auth"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *LoginOauth2AuthURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *LoginOauth2AuthURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *LoginOauth2AuthURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on LoginOauth2AuthURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on LoginOauth2AuthURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *LoginOauth2AuthURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -66,7 +66,7 @@ type LoginDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginDefault creates LoginDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *LoginDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login default response
|
||||
func (o *LoginDefault) WithPayload(payload *models.Error) *LoginDefault {
|
||||
func (o *LoginDefault) WithPayload(payload *models.APIError) *LoginDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login default response
|
||||
func (o *LoginDefault) SetPayload(payload *models.Error) {
|
||||
func (o *LoginDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
88
api/operations/auth/logout.go
Normal file
88
api/operations/auth/logout.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LogoutHandlerFunc turns a function with the right signature into a logout handler
|
||||
type LogoutHandlerFunc func(LogoutParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LogoutHandlerFunc) Handle(params LogoutParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// LogoutHandler interface for that can handle valid logout params
|
||||
type LogoutHandler interface {
|
||||
Handle(LogoutParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLogout creates a new http.Handler for the logout operation
|
||||
func NewLogout(ctx *middleware.Context, handler LogoutHandler) *Logout {
|
||||
return &Logout{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
Logout swagger:route POST /logout Auth logout
|
||||
|
||||
Logout from Console.
|
||||
*/
|
||||
type Logout struct {
|
||||
Context *middleware.Context
|
||||
Handler LogoutHandler
|
||||
}
|
||||
|
||||
func (o *Logout) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewLogoutParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
101
api/operations/auth/logout_parameters.go
Normal file
101
api/operations/auth/logout_parameters.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewLogoutParams creates a new LogoutParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewLogoutParams() LogoutParams {
|
||||
|
||||
return LogoutParams{}
|
||||
}
|
||||
|
||||
// LogoutParams contains all the bound params for the logout operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters Logout
|
||||
type LogoutParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.LogoutRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewLogoutParams() beforehand.
|
||||
func (o *LogoutParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.LogoutRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
115
api/operations/auth/logout_responses.go
Normal file
115
api/operations/auth/logout_responses.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// LogoutOKCode is the HTTP code returned for type LogoutOK
|
||||
const LogoutOKCode int = 200
|
||||
|
||||
/*
|
||||
LogoutOK A successful response.
|
||||
|
||||
swagger:response logoutOK
|
||||
*/
|
||||
type LogoutOK struct {
|
||||
}
|
||||
|
||||
// NewLogoutOK creates LogoutOK with default headers values
|
||||
func NewLogoutOK() *LogoutOK {
|
||||
|
||||
return &LogoutOK{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LogoutOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(200)
|
||||
}
|
||||
|
||||
/*
|
||||
LogoutDefault Generic error response.
|
||||
|
||||
swagger:response logoutDefault
|
||||
*/
|
||||
type LogoutDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLogoutDefault creates LogoutDefault with default headers values
|
||||
func NewLogoutDefault(code int) *LogoutDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &LogoutDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the logout default response
|
||||
func (o *LogoutDefault) WithStatusCode(code int) *LogoutDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the logout default response
|
||||
func (o *LogoutDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the logout default response
|
||||
func (o *LogoutDefault) WithPayload(payload *models.APIError) *LogoutDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the logout default response
|
||||
func (o *LogoutDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LogoutDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
api/operations/auth/logout_urlbuilder.go
Normal file
104
api/operations/auth/logout_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// LogoutURL generates an URL for the logout operation
|
||||
type LogoutURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LogoutURL) WithBasePath(bp string) *LogoutURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LogoutURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *LogoutURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/logout"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *LogoutURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *LogoutURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *LogoutURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on LogoutURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on LogoutURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *LogoutURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
88
api/operations/auth/session_check.go
Normal file
88
api/operations/auth/session_check.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SessionCheckHandlerFunc turns a function with the right signature into a session check handler
|
||||
type SessionCheckHandlerFunc func(SessionCheckParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn SessionCheckHandlerFunc) Handle(params SessionCheckParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// SessionCheckHandler interface for that can handle valid session check params
|
||||
type SessionCheckHandler interface {
|
||||
Handle(SessionCheckParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewSessionCheck creates a new http.Handler for the session check operation
|
||||
func NewSessionCheck(ctx *middleware.Context, handler SessionCheckHandler) *SessionCheck {
|
||||
return &SessionCheck{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
SessionCheck swagger:route GET /session Auth sessionCheck
|
||||
|
||||
Endpoint to check if your session is still valid
|
||||
*/
|
||||
type SessionCheck struct {
|
||||
Context *middleware.Context
|
||||
Handler SessionCheckHandler
|
||||
}
|
||||
|
||||
func (o *SessionCheck) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewSessionCheckParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
63
api/operations/auth/session_check_parameters.go
Normal file
63
api/operations/auth/session_check_parameters.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// NewSessionCheckParams creates a new SessionCheckParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewSessionCheckParams() SessionCheckParams {
|
||||
|
||||
return SessionCheckParams{}
|
||||
}
|
||||
|
||||
// SessionCheckParams contains all the bound params for the session check operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters SessionCheck
|
||||
type SessionCheckParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewSessionCheckParams() beforehand.
|
||||
func (o *SessionCheckParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
135
api/operations/auth/session_check_responses.go
Normal file
135
api/operations/auth/session_check_responses.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// SessionCheckOKCode is the HTTP code returned for type SessionCheckOK
|
||||
const SessionCheckOKCode int = 200
|
||||
|
||||
/*
|
||||
SessionCheckOK A successful response.
|
||||
|
||||
swagger:response sessionCheckOK
|
||||
*/
|
||||
type SessionCheckOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.SessionResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSessionCheckOK creates SessionCheckOK with default headers values
|
||||
func NewSessionCheckOK() *SessionCheckOK {
|
||||
|
||||
return &SessionCheckOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the session check o k response
|
||||
func (o *SessionCheckOK) WithPayload(payload *models.SessionResponse) *SessionCheckOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the session check o k response
|
||||
func (o *SessionCheckOK) SetPayload(payload *models.SessionResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SessionCheckOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SessionCheckDefault Generic error response.
|
||||
|
||||
swagger:response sessionCheckDefault
|
||||
*/
|
||||
type SessionCheckDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSessionCheckDefault creates SessionCheckDefault with default headers values
|
||||
func NewSessionCheckDefault(code int) *SessionCheckDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &SessionCheckDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the session check default response
|
||||
func (o *SessionCheckDefault) WithStatusCode(code int) *SessionCheckDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the session check default response
|
||||
func (o *SessionCheckDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the session check default response
|
||||
func (o *SessionCheckDefault) WithPayload(payload *models.APIError) *SessionCheckDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the session check default response
|
||||
func (o *SessionCheckDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *SessionCheckDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
api/operations/auth/session_check_urlbuilder.go
Normal file
104
api/operations/auth/session_check_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package auth
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// SessionCheckURL generates an URL for the session check operation
|
||||
type SessionCheckURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SessionCheckURL) WithBasePath(bp string) *SessionCheckURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *SessionCheckURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *SessionCheckURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/session"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *SessionCheckURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *SessionCheckURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *SessionCheckURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on SessionCheckURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on SessionCheckURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *SessionCheckURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user