Compare commits
445 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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 }}
|
||||
1207
.github/workflows/jobs.yaml
vendored
1207
.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.22
|
||||
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.22 ]
|
||||
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
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,12 +1,12 @@
|
||||
# Playwright Data
|
||||
portal-ui/storage/
|
||||
portal-ui/playwright/.auth/admin.json
|
||||
web-app/storage/
|
||||
web-app/playwright/.auth/admin.json
|
||||
|
||||
# Report from Playwright
|
||||
portal-ui/playwright-report/
|
||||
web-app/playwright-report/
|
||||
|
||||
# Coverage from Playwright
|
||||
portal-ui/.nyc_output/
|
||||
web-app/.nyc_output/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
@@ -37,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
|
||||
@@ -46,4 +46,4 @@ run:
|
||||
skip-dirs:
|
||||
- pkg/clientgen
|
||||
- pkg/apis/networking.gke.io
|
||||
- restapi/operations
|
||||
- api/operations
|
||||
|
||||
191
.goreleaser.yml
191
.goreleaser.yml
@@ -1,191 +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.19
|
||||
|
||||
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/
|
||||
|
||||
432
CHANGELOG.md
Normal file
432
CHANGELOG.md
Normal file
@@ -0,0 +1,432 @@
|
||||
# Changelog
|
||||
|
||||
## 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,32 @@
|
||||
# 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
|
||||
@@ -48,25 +77,6 @@ Still in the MinIO folder, run
|
||||
make build
|
||||
```
|
||||
|
||||
# Testing on Kubernetes
|
||||
|
||||
If you want to test console on kubernetes, you can perform all the steps from `Building with MinIO`, but change `Step 3`
|
||||
to the following:
|
||||
|
||||
```shell
|
||||
TAG=miniodev/console:dev make docker
|
||||
```
|
||||
|
||||
This will build a docker container image that can be used to test with your local kubernetes environment.
|
||||
|
||||
For example, if you are using kind:
|
||||
|
||||
```shell
|
||||
kind load docker-image miniodev/console:dev
|
||||
```
|
||||
|
||||
and then deploy any `Tenant` that uses this image
|
||||
|
||||
# LDAP authentication with Console
|
||||
|
||||
## Setup
|
||||
|
||||
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.19 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.7
|
||||
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,27 +0,0 @@
|
||||
FROM --platform=linux/amd64 registry.access.redhat.com/ubi8/ubi-minimal:8.7 as build
|
||||
|
||||
RUN microdnf update --nodocs && microdnf install ca-certificates --nodocs
|
||||
RUN curl -s -q https://raw.githubusercontent.com/minio/kes/master/LICENSE -o LICENSE
|
||||
RUN curl -s -q https://raw.githubusercontent.com/minio/kes/master/CREDITS -o CREDITS
|
||||
|
||||
FROM registry.access.redhat.com/ubi8/ubi-micro:8.7
|
||||
|
||||
# On RHEL the certificate bundle is located at:
|
||||
# - /etc/pki/tls/certs/ca-bundle.crt (RHEL 6)
|
||||
# - /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (RHEL 7)
|
||||
COPY --from=build /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /etc/pki/ca-trust/extracted/pem/
|
||||
COPY --from=build LICENSE /LICENSE
|
||||
COPY --from=build CREDITS /CREDITS
|
||||
|
||||
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."
|
||||
|
||||
EXPOSE 9090
|
||||
COPY console /console
|
||||
|
||||
ENTRYPOINT ["/console"]
|
||||
78
Makefile
78
Makefile
@@ -33,6 +33,10 @@ 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
|
||||
@@ -48,18 +52,19 @@ apply-gofmt:
|
||||
clean-swagger:
|
||||
@echo "cleaning"
|
||||
@rm -rf models
|
||||
@rm -rf restapi/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.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 ./portal-ui/src/api -n consoleApi.ts
|
||||
@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
|
||||
|
||||
|
||||
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 . --loglevel warn; cd ..)
|
||||
|
||||
test-integration:
|
||||
@(docker stop pgsqlcontainer || true)
|
||||
@@ -77,7 +82,7 @@ test-integration:
|
||||
@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 && ./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)
|
||||
@@ -125,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)
|
||||
@@ -179,45 +184,40 @@ test-sso-integration:
|
||||
@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)
|
||||
@(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})
|
||||
@@ -226,20 +226,50 @@ 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)
|
||||
|
||||
|
||||
# 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"
|
||||
@@ -254,4 +284,4 @@ release: swagger-gen
|
||||
@echo "Generating Release: $(RELEASE)"
|
||||
@make assets
|
||||
@git add -u .
|
||||
@git add portal-ui/build/
|
||||
@git add web-app/build/
|
||||
|
||||
70
README.md
70
README.md
@@ -12,53 +12,30 @@ A graphical user interface for [MinIO](https://github.com/minio/minio)
|
||||
**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
|
||||
MinIO Console is a library that provides a management and browser UI overlay for the MinIO Server.
|
||||
The standalone binary installation path has been removed.
|
||||
|
||||
| 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
|
||||
```
|
||||
In case a Console standalone binary is needed, it can be generated by building this package from source as follows:
|
||||
|
||||
### 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.19
|
||||
> Minimum version required is go1.22
|
||||
|
||||
```
|
||||
go install github.com/minio/console/cmd/console@latest
|
||||
@@ -228,6 +205,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)
|
||||
|
||||
@@ -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,12 +25,12 @@ 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"
|
||||
)
|
||||
@@ -39,7 +39,7 @@ func TestArnsList(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
// 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"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type AdminClientMock struct{}
|
||||
@@ -47,7 +47,7 @@ var (
|
||||
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, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, 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)
|
||||
@@ -66,10 +66,12 @@ var (
|
||||
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
|
||||
minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) 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
|
||||
|
||||
@@ -80,32 +82,36 @@ var (
|
||||
minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
|
||||
|
||||
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
|
||||
minioAddServiceAccountMock func(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, 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(_ context.Context, _, _ string) (targets []madmin.BucketTarget, err error) {
|
||||
return nil, nil
|
||||
func (ac AdminClientMock) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
|
||||
return minioListRemoteBucketsMock(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getRemoteBucket(_ context.Context, _, _ string) (targets *madmin.BucketTarget, err error) {
|
||||
return nil, nil
|
||||
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(_ context.Context, _ string, _ *madmin.BucketTarget) (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 {
|
||||
@@ -116,8 +122,8 @@ func (ac AdminClientMock) speedtest(_ context.Context, _ madmin.SpeedtestOpts) (
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) verifyTierStatus(_ context.Context, _ string) error {
|
||||
return nil
|
||||
func (ac AdminClientMock) verifyTierStatus(ctx context.Context, tier string) error {
|
||||
return minioVerifyTierStatusMock(ctx, tier)
|
||||
}
|
||||
|
||||
// mock function helpConfigKV()
|
||||
@@ -170,8 +176,8 @@ func (ac AdminClientMock) heal(ctx context.Context, bucket, prefix string, healO
|
||||
return minioHealMock(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) serverHealthInfo(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error) {
|
||||
return minioServerHealthInfoMock(ctx, healthDataTypes, deadline)
|
||||
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) {
|
||||
@@ -210,10 +216,6 @@ func (ac AdminClientMock) createKey(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) importKey(_ context.Context, _ string, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listKeys(_ context.Context, _ string) ([]madmin.KMSKeyInfo, error) {
|
||||
return []madmin.KMSKeyInfo{{
|
||||
Name: "name",
|
||||
@@ -225,55 +227,6 @@ func (ac AdminClientMock) keyStatus(_ context.Context, _ string) (*madmin.KMSKey
|
||||
return &madmin.KMSKeyStatus{KeyID: "key"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteKey(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setKMSPolicy(_ context.Context, _ string, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) assignPolicy(_ context.Context, _ string, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) describePolicy(_ context.Context, _ string) (*madmin.KMSDescribePolicy, error) {
|
||||
return &madmin.KMSDescribePolicy{Name: "name"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getKMSPolicy(_ context.Context, _ string) (*madmin.KMSPolicy, error) {
|
||||
return &madmin.KMSPolicy{Allow: []string{""}, Deny: []string{""}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listKMSPolicies(_ context.Context, _ string) ([]madmin.KMSPolicyInfo, error) {
|
||||
return []madmin.KMSPolicyInfo{{
|
||||
Name: "name",
|
||||
CreatedBy: "by",
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deletePolicy(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) describeIdentity(_ context.Context, _ string) (*madmin.KMSDescribeIdentity, error) {
|
||||
return &madmin.KMSDescribeIdentity{}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) describeSelfIdentity(_ context.Context) (*madmin.KMSDescribeSelfIdentity, error) {
|
||||
return &madmin.KMSDescribeSelfIdentity{
|
||||
Policy: &madmin.KMSPolicy{Allow: []string{}, Deny: []string{}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteIdentity(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listIdentities(_ context.Context, _ string) ([]madmin.KMSIdentityInfo, error) {
|
||||
return []madmin.KMSIdentityInfo{{Identity: "identity"}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listPolicies(_ context.Context) (map[string]*iampolicy.Policy, error) {
|
||||
return minioListPoliciesMock()
|
||||
}
|
||||
@@ -313,11 +266,11 @@ func (ac AdminClientMock) getSiteReplicationInfo(ctx context.Context) (*madmin.S
|
||||
return getSiteReplicationInfo(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite) (*madmin.ReplicateAddStatus, error) {
|
||||
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.ReplicateEditStatus, error) {
|
||||
func (ac AdminClientMock) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, _ madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
|
||||
return editSiteReplicationInfo(ctx, site)
|
||||
}
|
||||
|
||||
@@ -341,6 +294,10 @@ func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig)
|
||||
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)
|
||||
}
|
||||
@@ -373,8 +330,8 @@ func (ac AdminClientMock) AccountInfo(ctx context.Context) (madmin.AccountInfo,
|
||||
return minioAccountInfoMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addServiceAccount(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error) {
|
||||
return minioAddServiceAccountMock(ctx, policy, user, accessKey, secretKey)
|
||||
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) {
|
||||
@@ -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,11 +24,11 @@ import (
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
madmin "github.com/minio/madmin-go/v2"
|
||||
madmin "github.com/minio/madmin-go/v3"
|
||||
|
||||
cfgApi "github.com/minio/console/restapi/operations/configuration"
|
||||
cfgApi "github.com/minio/console/api/operations/configuration"
|
||||
)
|
||||
|
||||
func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
@@ -36,7 +36,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationListConfigHandler = cfgApi.ListConfigHandlerFunc(func(params cfgApi.ListConfigParams, session *models.Principal) middleware.Responder {
|
||||
configListResp, err := getListConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewListConfigDefault(int(err.Code)).WithPayload(err)
|
||||
return cfgApi.NewListConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewListConfigOK().WithPayload(configListResp)
|
||||
})
|
||||
@@ -44,7 +44,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationConfigInfoHandler = cfgApi.ConfigInfoHandlerFunc(func(params cfgApi.ConfigInfoParams, session *models.Principal) middleware.Responder {
|
||||
config, err := getConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewConfigInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return cfgApi.NewConfigInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewConfigInfoOK().WithPayload(config)
|
||||
})
|
||||
@@ -52,7 +52,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationSetConfigHandler = cfgApi.SetConfigHandlerFunc(func(params cfgApi.SetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := setConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewSetConfigDefault(int(err.Code)).WithPayload(err)
|
||||
return cfgApi.NewSetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewSetConfigOK().WithPayload(resp)
|
||||
})
|
||||
@@ -60,7 +60,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationResetConfigHandler = cfgApi.ResetConfigHandlerFunc(func(params cfgApi.ResetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := resetConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewResetConfigDefault(int(err.Code)).WithPayload(err)
|
||||
return cfgApi.NewResetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewResetConfigOK().WithPayload(resp)
|
||||
})
|
||||
@@ -68,14 +68,14 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.ConfigurationExportConfigHandler = cfgApi.ExportConfigHandlerFunc(func(params cfgApi.ExportConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := exportConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewExportConfigDefault((int(err.Code))).WithPayload(err)
|
||||
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((int(err.Code))).WithPayload(err)
|
||||
return cfgApi.NewPostConfigsImportDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewPostConfigsImportDefault(200)
|
||||
})
|
||||
@@ -101,10 +101,10 @@ func listConfig(client MinioAdmin) ([]*models.ConfigDescription, error) {
|
||||
}
|
||||
|
||||
// getListConfigResponse performs listConfig() and serializes it to the handler's output
|
||||
func getListConfigResponse(session *models.Principal, params cfgApi.ListConfigParams) (*models.ListConfigResponse, *models.Error) {
|
||||
func getListConfigResponse(session *models.Principal, params cfgApi.ListConfigParams) (*models.ListConfigResponse, *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)
|
||||
}
|
||||
@@ -166,10 +166,10 @@ func getConfig(ctx context.Context, client MinioAdmin, name string) ([]*models.C
|
||||
}
|
||||
|
||||
// getConfigResponse performs getConfig() and serializes it to the handler's output
|
||||
func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams) ([]*models.Configuration, *models.Error) {
|
||||
func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams) ([]*models.Configuration, *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)
|
||||
}
|
||||
@@ -230,11 +230,11 @@ func buildConfig(configName *string, kvs []*models.ConfigurationKV) *string {
|
||||
}
|
||||
|
||||
// setConfigResponse implements setConfig() to be used by handler
|
||||
func setConfigResponse(session *models.Principal, params cfgApi.SetConfigParams) (*models.SetConfigResponse, *models.Error) {
|
||||
func setConfigResponse(session *models.Principal, params cfgApi.SetConfigParams) (*models.SetConfigResponse, *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)
|
||||
}
|
||||
@@ -256,11 +256,11 @@ func resetConfig(ctx context.Context, client MinioAdmin, configName *string) (er
|
||||
}
|
||||
|
||||
// resetConfigResponse implements resetConfig() to be used by handler
|
||||
func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigParams) (*models.SetConfigResponse, *models.Error) {
|
||||
func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigParams) (*models.SetConfigResponse, *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)
|
||||
}
|
||||
@@ -269,7 +269,6 @@ func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigPar
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
err = resetConfig(ctx, adminClient, ¶ms.Name)
|
||||
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -277,11 +276,11 @@ func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigPar
|
||||
return &models.SetConfigResponse{Restart: true}, nil
|
||||
}
|
||||
|
||||
func exportConfigResponse(session *models.Principal, params cfgApi.ExportConfigParams) (*models.ConfigExportResponse, *models.Error) {
|
||||
func exportConfigResponse(session *models.Principal, params cfgApi.ExportConfigParams) (*models.ConfigExportResponse, *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)
|
||||
}
|
||||
@@ -296,19 +295,19 @@ func exportConfigResponse(session *models.Principal, params cfgApi.ExportConfigP
|
||||
}, nil
|
||||
}
|
||||
|
||||
func importConfigResponse(session *models.Principal, params cfgApi.PostConfigsImportParams) (*cfgApi.PostConfigsImportDefault, *models.Error) {
|
||||
func importConfigResponse(session *models.Principal, params cfgApi.PostConfigsImportParams) (*cfgApi.PostConfigsImportDefault, *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)
|
||||
}
|
||||
file, _, err := params.HTTPRequest.FormFile("file")
|
||||
defer file.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = mAdmin.SetConfig(ctx, file)
|
||||
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"
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -63,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)
|
||||
@@ -80,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)
|
||||
@@ -94,7 +94,7 @@ func TestSetConfig(t *testing.T) {
|
||||
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"
|
||||
@@ -119,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)
|
||||
@@ -129,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)
|
||||
@@ -144,7 +144,7 @@ func TestDelConfig(t *testing.T) {
|
||||
adminClient := AdminClientMock{}
|
||||
function := "resetConfig()"
|
||||
// mock function response from setConfig()
|
||||
minioDelConfigKVMock = func(name string) (err error) {
|
||||
minioDelConfigKVMock = func(_ string) (err error) {
|
||||
return nil
|
||||
}
|
||||
configName := "region"
|
||||
@@ -158,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")
|
||||
}
|
||||
|
||||
@@ -220,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)
|
||||
}
|
||||
@@ -260,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,
|
||||
@@ -280,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,
|
||||
@@ -300,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,
|
||||
@@ -320,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,
|
||||
@@ -328,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)
|
||||
@@ -361,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
|
||||
}
|
||||
|
||||
@@ -407,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
|
||||
}
|
||||
},
|
||||
@@ -435,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
|
||||
}
|
||||
|
||||
@@ -481,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
|
||||
}
|
||||
},
|
||||
@@ -496,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)
|
||||
@@ -545,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
|
||||
}
|
||||
},
|
||||
@@ -560,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
|
||||
}
|
||||
},
|
||||
@@ -581,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")
|
||||
}
|
||||
},
|
||||
@@ -595,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,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
@@ -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,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -40,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) {
|
||||
@@ -58,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)
|
||||
@@ -82,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) {
|
||||
@@ -91,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) {
|
||||
@@ -108,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,7 +24,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -130,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()"
|
||||
@@ -144,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)
|
||||
@@ -226,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
|
||||
}
|
||||
@@ -236,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)
|
||||
@@ -258,7 +258,7 @@ func TestSetGroupStatus(t *testing.T) {
|
||||
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 {
|
||||
@@ -266,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 {
|
||||
@@ -274,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) {
|
||||
@@ -282,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) {
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
"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/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/idp"
|
||||
madmin "github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
var errInvalidIDPType = fmt.Errorf("IDP type must be one of %v", madmin.ValidIDPConfigTypes)
|
||||
@@ -35,51 +35,51 @@ 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(int(err.Code)).WithPayload(err)
|
||||
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(int(err.Code)).WithPayload(err)
|
||||
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(int(err.Code)).WithPayload(err)
|
||||
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(int(err.Code)).WithPayload(err)
|
||||
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(int(err.Code)).WithPayload(err)
|
||||
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(int(err.Code)).WithPayload(err)
|
||||
return idp.NewGetLDAPEntitiesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewGetLDAPEntitiesOK().WithPayload(response)
|
||||
})
|
||||
}
|
||||
|
||||
func createIDPConfigurationResponse(session *models.Principal, params idp.CreateConfigurationParams) (*models.SetIDPResponse, *models.Error) {
|
||||
func createIDPConfigurationResponse(session *models.Principal, params idp.CreateConfigurationParams) (*models.SetIDPResponse, *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)
|
||||
}
|
||||
@@ -90,10 +90,10 @@ func createIDPConfigurationResponse(session *models.Principal, params idp.Create
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func updateIDPConfigurationResponse(session *models.Principal, params idp.UpdateConfigurationParams) (*models.SetIDPResponse, *models.Error) {
|
||||
func updateIDPConfigurationResponse(session *models.Principal, params idp.UpdateConfigurationParams) (*models.SetIDPResponse, *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)
|
||||
}
|
||||
@@ -115,10 +115,10 @@ func createOrUpdateIDPConfig(ctx context.Context, idpType, name, input string, u
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func listIDPConfigurationsResponse(session *models.Principal, params idp.ListConfigurationsParams) (*models.IdpListConfigurationsResponse, *models.Error) {
|
||||
func listIDPConfigurationsResponse(session *models.Principal, params idp.ListConfigurationsParams) (*models.IdpListConfigurationsResponse, *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)
|
||||
}
|
||||
@@ -151,10 +151,10 @@ func parseIDPConfigurations(configs []madmin.IDPListItem) (serverConfigs []*mode
|
||||
return serverConfigs
|
||||
}
|
||||
|
||||
func deleteIDPConfigurationResponse(session *models.Principal, params idp.DeleteConfigurationParams) (*models.SetIDPResponse, *models.Error) {
|
||||
func deleteIDPConfigurationResponse(session *models.Principal, params idp.DeleteConfigurationParams) (*models.SetIDPResponse, *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)
|
||||
}
|
||||
@@ -176,10 +176,10 @@ func deleteIDPConfig(ctx context.Context, idpType, name string, client MinioAdmi
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func getIDPConfigurationsResponse(session *models.Principal, params idp.GetConfigurationParams) (*models.IdpServerConfiguration, *models.Error) {
|
||||
func getIDPConfigurationsResponse(session *models.Principal, params idp.GetConfigurationParams) (*models.IdpServerConfiguration, *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)
|
||||
}
|
||||
@@ -198,6 +198,7 @@ func getIDPConfiguration(ctx context.Context, idpType, name string, client Minio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.IdpServerConfiguration{
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
@@ -217,10 +218,10 @@ func parseIDPConfigurationsInfo(infoList []madmin.IDPCfgInfo) (results []*models
|
||||
return results
|
||||
}
|
||||
|
||||
func getLDAPEntitiesResponse(session *models.Principal, params idp.GetLDAPEntitiesParams) (*models.LdapEntities, *models.Error) {
|
||||
func getLDAPEntitiesResponse(session *models.Principal, params idp.GetLDAPEntitiesParams) (*models.LdapEntities, *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,11 +25,11 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"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/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/idp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
@@ -46,7 +46,7 @@ type IDPTestSuite struct {
|
||||
func (suite *IDPTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func TestGetEntitiesResult(t *testing.T) {
|
||||
GroupMappings: groupsMap,
|
||||
UserMappings: usersMap,
|
||||
}
|
||||
minioGetLDAPPolicyEntitiesMock = func(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ func TestGetEntitiesResult(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test-2: getEntitiesResult error is returned from getLDAPPolicyEntities()
|
||||
minioGetLDAPPolicyEntitiesMock = func(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return madmin.PolicyEntitiesResult{}, errors.New("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"
|
||||
@@ -28,11 +28,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"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 registerAdminInfoHandlers(api *operations.ConsoleAPI) {
|
||||
@@ -40,15 +41,15 @@ func registerAdminInfoHandlers(api *operations.ConsoleAPI) {
|
||||
api.SystemAdminInfoHandler = systemApi.AdminInfoHandlerFunc(func(params systemApi.AdminInfoParams, session *models.Principal) middleware.Responder {
|
||||
infoResp, err := getAdminInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewAdminInfoDefault(int(err.Code)).WithPayload(err)
|
||||
return systemApi.NewAdminInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return systemApi.NewAdminInfoOK().WithPayload(infoResp)
|
||||
})
|
||||
// return single widget results
|
||||
api.SystemDashboardWidgetDetailsHandler = systemApi.DashboardWidgetDetailsHandlerFunc(func(params systemApi.DashboardWidgetDetailsParams, session *models.Principal) middleware.Responder {
|
||||
api.SystemDashboardWidgetDetailsHandler = systemApi.DashboardWidgetDetailsHandlerFunc(func(params systemApi.DashboardWidgetDetailsParams, _ *models.Principal) middleware.Responder {
|
||||
infoResp, err := getAdminInfoWidgetResponse(params)
|
||||
if err != nil {
|
||||
return systemApi.NewDashboardWidgetDetailsDefault(int(err.Code)).WithPayload(err)
|
||||
return systemApi.NewDashboardWidgetDetailsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return systemApi.NewDashboardWidgetDetailsOK().WithPayload(infoResp)
|
||||
})
|
||||
@@ -58,7 +59,7 @@ type UsageInfo struct {
|
||||
Buckets int64
|
||||
Objects int64
|
||||
Usage int64
|
||||
DisksUsage int64
|
||||
DrivesUsage int64
|
||||
Servers []*models.ServerProperties
|
||||
EndpointNotReady bool
|
||||
Backend *models.BackendProperties
|
||||
@@ -73,34 +74,11 @@ func GetAdminInfo(ctx context.Context, client MinioAdmin) (*UsageInfo, error) {
|
||||
// we are trimming uint64 to int64 this will report an incorrect measurement for numbers greater than
|
||||
// 9,223,372,036,854,775,807
|
||||
|
||||
var backendType string
|
||||
var rrSCParity float64
|
||||
var standardSCParity float64
|
||||
var onlineDrives float64
|
||||
var offlineDrives float64
|
||||
|
||||
if v, success := serverInfo.Backend.(map[string]interface{}); success {
|
||||
bt, ok := v["backendType"]
|
||||
if ok {
|
||||
backendType = bt.(string)
|
||||
}
|
||||
rp, ok := v["rrSCParity"]
|
||||
if ok {
|
||||
rrSCParity = rp.(float64)
|
||||
}
|
||||
sp, ok := v["standardSCParity"]
|
||||
if ok {
|
||||
standardSCParity = sp.(float64)
|
||||
}
|
||||
onDrives, ok := v["onlineDisks"]
|
||||
if ok {
|
||||
onlineDrives = onDrives.(float64)
|
||||
}
|
||||
offDrives, ok := v["offlineDisks"]
|
||||
if ok {
|
||||
offlineDrives = offDrives.(float64)
|
||||
}
|
||||
}
|
||||
backendType := serverInfo.Backend.Type
|
||||
rrSCParity := serverInfo.Backend.RRSCParity
|
||||
standardSCParity := serverInfo.Backend.StandardSCParity
|
||||
onlineDrives := serverInfo.Backend.OnlineDisks
|
||||
offlineDrives := serverInfo.Backend.OfflineDisks
|
||||
|
||||
var usedSpace int64
|
||||
// serverArray contains the serverProperties which describe the servers in the network
|
||||
@@ -139,19 +117,19 @@ func GetAdminInfo(ctx context.Context, client MinioAdmin) (*UsageInfo, error) {
|
||||
}
|
||||
|
||||
backendData := &models.BackendProperties{
|
||||
BackendType: backendType,
|
||||
BackendType: string(backendType),
|
||||
RrSCParity: int64(rrSCParity),
|
||||
StandardSCParity: int64(standardSCParity),
|
||||
OnlineDrives: int64(onlineDrives),
|
||||
OfflineDrives: int64(offlineDrives),
|
||||
}
|
||||
return &UsageInfo{
|
||||
Buckets: int64(serverInfo.Buckets.Count),
|
||||
Objects: int64(serverInfo.Objects.Count),
|
||||
Usage: int64(serverInfo.Usage.Size),
|
||||
DisksUsage: usedSpace,
|
||||
Servers: serverArray,
|
||||
Backend: backendData,
|
||||
Buckets: int64(serverInfo.Buckets.Count),
|
||||
Objects: int64(serverInfo.Objects.Count),
|
||||
Usage: int64(serverInfo.Usage.Size),
|
||||
DrivesUsage: usedSpace,
|
||||
Servers: serverArray,
|
||||
Backend: backendData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -194,7 +172,7 @@ type WidgetLabel struct {
|
||||
|
||||
var labels = []WidgetLabel{
|
||||
{Name: "instance"},
|
||||
{Name: "disk"},
|
||||
{Name: "drive"},
|
||||
{Name: "server"},
|
||||
{Name: "api"},
|
||||
}
|
||||
@@ -326,7 +304,7 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `sum(minio_bucket_usage_total_bytes{$__query}) by (instance)`,
|
||||
Expr: `minio_cluster_usage_total_bytes{$__query}`,
|
||||
LegendFormat: "Used Capacity",
|
||||
InitialTime: -180,
|
||||
Step: 10,
|
||||
@@ -352,7 +330,7 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `max by (range) (minio_bucket_objects_size_distribution{$__query})`,
|
||||
Expr: `minio_cluster_objects_size_distribution{$__query}`,
|
||||
LegendFormat: "{{range}}",
|
||||
Step: 300,
|
||||
},
|
||||
@@ -464,7 +442,7 @@ var widgets = []Metric{
|
||||
},
|
||||
{
|
||||
ID: 9,
|
||||
Title: "Total Online Disks",
|
||||
Title: "Total Online Drives",
|
||||
Type: "stat",
|
||||
MaxDataPoints: 100,
|
||||
GridPos: GridPos{
|
||||
@@ -482,8 +460,8 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `minio_cluster_disk_online_total{$__query}`,
|
||||
LegendFormat: "Total online disks in MinIO Cluster",
|
||||
Expr: `minio_cluster_drive_online_total{$__query}`,
|
||||
LegendFormat: "Total online drives in MinIO Cluster",
|
||||
Step: 60,
|
||||
},
|
||||
},
|
||||
@@ -492,7 +470,7 @@ var widgets = []Metric{
|
||||
ID: 66,
|
||||
Title: "Number of Buckets",
|
||||
Type: "stat",
|
||||
MaxDataPoints: 100,
|
||||
MaxDataPoints: 5,
|
||||
GridPos: GridPos{
|
||||
H: 3,
|
||||
W: 3,
|
||||
@@ -508,8 +486,9 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `count(count by (bucket) (minio_bucket_usage_total_bytes{$__query}))`,
|
||||
Expr: `minio_cluster_bucket_total{$__query}`,
|
||||
LegendFormat: "",
|
||||
Step: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -575,7 +554,7 @@ var widgets = []Metric{
|
||||
},
|
||||
{
|
||||
ID: 78,
|
||||
Title: "Total Offline Disks",
|
||||
Title: "Total Offline Drives",
|
||||
Type: "stat",
|
||||
MaxDataPoints: 100,
|
||||
GridPos: GridPos{
|
||||
@@ -593,7 +572,7 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `minio_cluster_disk_offline_total{$__query}`,
|
||||
Expr: `minio_cluster_drive_offline_total{$__query}`,
|
||||
LegendFormat: "",
|
||||
Step: 60,
|
||||
},
|
||||
@@ -619,7 +598,7 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `topk(1, sum(minio_bucket_usage_object_total{$__query}) by (instance))`,
|
||||
Expr: `minio_cluster_usage_object_total{$__query}`,
|
||||
LegendFormat: "",
|
||||
},
|
||||
},
|
||||
@@ -779,8 +758,8 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `minio_node_disk_used_bytes{$__query}`,
|
||||
LegendFormat: "Used Capacity [{{server}}:{{disk}}]",
|
||||
Expr: `minio_node_drive_used_bytes{$__query}`,
|
||||
LegendFormat: "Used Capacity [{{server}}:{{drive}}]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -796,8 +775,8 @@ var widgets = []Metric{
|
||||
},
|
||||
Targets: []Target{
|
||||
{
|
||||
Expr: `minio_cluster_disk_free_inodes{$__query}`,
|
||||
LegendFormat: "Free Inodes [{{server}}:{{disk}}]",
|
||||
Expr: `minio_node_drive_free_inodes{$__query}`,
|
||||
LegendFormat: "Free Inodes [{{server}}:{{drive}}]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -858,8 +837,8 @@ var widgets = []Metric{
|
||||
},
|
||||
|
||||
{
|
||||
Expr: `rate(minio_node_io_rchar_bytes{$__query}[$__rate_interval])`,
|
||||
LegendFormat: "Node RChar [{{server}}]",
|
||||
Expr: `rate(minio_node_io_wchar_bytes{$__query}[$__rate_interval])`,
|
||||
LegendFormat: "Node WChar [{{server}}]",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -896,16 +875,19 @@ type LabelResults struct {
|
||||
}
|
||||
|
||||
// getAdminInfoResponse returns the response containing total buckets, objects and usage.
|
||||
func getAdminInfoResponse(session *models.Principal, params systemApi.AdminInfoParams) (*models.AdminInfoResponse, *models.Error) {
|
||||
func getAdminInfoResponse(session *models.Principal, params systemApi.AdminInfoParams) (*models.AdminInfoResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
prometheusURL := ""
|
||||
|
||||
if !*params.DefaultOnly {
|
||||
prometheusURL = getPrometheusURL()
|
||||
promURL := getPrometheusURL()
|
||||
if promURL != "" {
|
||||
prometheusURL = promURL
|
||||
}
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -977,15 +959,19 @@ func getUsageWidgetsForDeployment(ctx context.Context, prometheusURL string, adm
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool {
|
||||
httpClnt := GetConsoleHTTPClient(endpoint)
|
||||
|
||||
func unmarshalPrometheus(ctx context.Context, httpClnt *http.Client, endpoint string, data interface{}) bool {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to create the request to fetch labels from prometheus: %w", err))
|
||||
return true
|
||||
}
|
||||
|
||||
prometheusBearer := getPrometheusAuthToken()
|
||||
|
||||
if prometheusBearer != "" {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", prometheusBearer))
|
||||
}
|
||||
|
||||
resp, err := httpClnt.Do(req)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus: %w", err))
|
||||
@@ -1013,7 +999,16 @@ func testPrometheusURL(ctx context.Context, url string) bool {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err))
|
||||
return false
|
||||
}
|
||||
response, err := GetConsoleHTTPClient(url).Do(req)
|
||||
|
||||
prometheusBearer := getPrometheusAuthToken()
|
||||
if prometheusBearer != "" {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", prometheusBearer))
|
||||
}
|
||||
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
httpClnt := GetConsoleHTTPClient(clientIP)
|
||||
|
||||
response, err := httpClnt.Do(req)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
|
||||
newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy"
|
||||
@@ -1022,7 +1017,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err))
|
||||
return false
|
||||
}
|
||||
rootResponse, err := GetConsoleHTTPClient(newTestURL).Do(req2)
|
||||
rootResponse, err := httpClnt.Do(req2)
|
||||
if err != nil {
|
||||
// URL & Root tests didn't work. Prometheus not reachable
|
||||
ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err))
|
||||
@@ -1033,7 +1028,7 @@ func testPrometheusURL(ctx context.Context, url string) bool {
|
||||
return response.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
func getAdminInfoWidgetResponse(params systemApi.DashboardWidgetDetailsParams) (*models.WidgetDetails, *models.Error) {
|
||||
func getAdminInfoWidgetResponse(params systemApi.DashboardWidgetDetailsParams) (*models.WidgetDetails, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
prometheusURL := getPrometheusURL()
|
||||
@@ -1044,14 +1039,19 @@ func getAdminInfoWidgetResponse(params systemApi.DashboardWidgetDetailsParams) (
|
||||
if strings.TrimSpace(prometheusExtraLabels) != "" {
|
||||
selector = fmt.Sprintf(`job="%s",%s`, prometheusJobID, prometheusExtraLabels)
|
||||
}
|
||||
clientIP := getClientIP(params.HTTPRequest)
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, clientIP)
|
||||
return getWidgetDetails(ctx, prometheusURL, selector, params.WidgetID, params.Step, params.Start, params.End)
|
||||
}
|
||||
|
||||
func getWidgetDetails(ctx context.Context, prometheusURL string, selector string, widgetID int32, step *int32, start *int64, end *int64) (*models.WidgetDetails, *models.Error) {
|
||||
func getWidgetDetails(ctx context.Context, prometheusURL string, selector string, widgetID int32, step *int32, start *int64, end *int64) (*models.WidgetDetails, *CodedAPIError) {
|
||||
// We test if prometheus URL is reachable. this is meant to avoid unuseful calls and application hang.
|
||||
if !testPrometheusURL(ctx, prometheusURL) {
|
||||
return nil, ErrorWithContext(ctx, errors.New("prometheus URL is unreachable"))
|
||||
}
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
httpClnt := GetConsoleHTTPClient(clientIP)
|
||||
|
||||
labelResultsCh := make(chan LabelResults)
|
||||
|
||||
for _, lbl := range labels {
|
||||
@@ -1059,7 +1059,7 @@ func getWidgetDetails(ctx context.Context, prometheusURL string, selector string
|
||||
endpoint := fmt.Sprintf("%s/api/v1/label/%s/values", prometheusURL, lbl.Name)
|
||||
|
||||
var response LabelResponse
|
||||
if unmarshalPrometheus(ctx, endpoint, &response) {
|
||||
if unmarshalPrometheus(ctx, httpClnt, endpoint, &response) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1145,7 +1145,7 @@ LabelsWaitLoop:
|
||||
endpoint := fmt.Sprintf("%s/api/v1/%s?query=%s%s", prometheusURL, apiType, url.QueryEscape(queryExpr), extraParamters)
|
||||
|
||||
var response PromResp
|
||||
if unmarshalPrometheus(ctx, endpoint, &response) {
|
||||
if unmarshalPrometheus(ctx, httpClnt, endpoint, &response) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1188,5 +1188,5 @@ LabelsWaitLoop:
|
||||
return &wdgtResult, nil
|
||||
}
|
||||
|
||||
return nil, &models.Error{Code: 404, Message: swag.String("Widget not found")}
|
||||
return nil, &CodedAPIError{Code: 404, APIError: &models.APIError{Message: "Widget not found"}}
|
||||
}
|
||||
@@ -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,10 +23,12 @@ import (
|
||||
"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/console/restapi/operations"
|
||||
systemApi "github.com/minio/console/restapi/operations/system"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
@@ -44,16 +46,12 @@ type AdminInfoTestSuite struct {
|
||||
func (suite *AdminInfoTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
MinioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{
|
||||
Servers: []madmin.ServerProperties{{
|
||||
Disks: []madmin.Disk{{}},
|
||||
}},
|
||||
Backend: map[string]interface{}{
|
||||
"backendType": "mock",
|
||||
"rrSCParity": 0.0,
|
||||
"standardSCParity": 0.0,
|
||||
},
|
||||
Backend: madmin.ErasureBackend{Type: "mock"},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -129,7 +127,7 @@ func (suite *AdminInfoTestSuite) initSystemDashboardWidgetDetailsRequest() (para
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetUsageWidgetsForDeploymentWithoutError() {
|
||||
ctx := context.Background()
|
||||
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)
|
||||
@@ -138,7 +136,7 @@ func (suite *AdminInfoTestSuite) TestGetUsageWidgetsForDeploymentWithoutError()
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetWidgetDetailsWithoutError() {
|
||||
ctx := context.Background()
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
suite.isPrometheusRequest = true
|
||||
var step int32 = 1
|
||||
var start int64
|
||||
@@ -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,8 +24,8 @@ 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) {
|
||||
@@ -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,7 +544,7 @@ 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 {
|
||||
@@ -571,7 +553,7 @@ func getAddPolicyResponse(session *models.Principal, params policyApi.AddPolicyP
|
||||
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 +595,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 +622,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 +640,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,7 +26,7 @@ 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"
|
||||
)
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestRemovePolicy(t *testing.T) {
|
||||
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()"
|
||||
@@ -91,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) {
|
||||
@@ -106,10 +106,10 @@ func TestAddPolicy(t *testing.T) {
|
||||
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 {
|
||||
@@ -138,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) {
|
||||
@@ -164,7 +164,7 @@ func TestSetPolicy(t *testing.T) {
|
||||
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
|
||||
@@ -181,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) {
|
||||
@@ -189,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) {
|
||||
@@ -219,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
|
||||
},
|
||||
},
|
||||
@@ -231,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")
|
||||
},
|
||||
},
|
||||
@@ -244,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
|
||||
},
|
||||
},
|
||||
@@ -252,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) {
|
||||
@@ -373,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)
|
||||
}
|
||||
@@ -14,15 +14,15 @@
|
||||
// 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"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ func startProfiling(ctx context.Context, conn WSConn, client MinioAdmin, pOpts *
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message, err := ioutil.ReadAll(zippedData)
|
||||
message, err := io.ReadAll(zippedData)
|
||||
if err != nil {
|
||||
return 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"
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestStartProfiling(t *testing.T) {
|
||||
|
||||
// Test-1 : startProfiling() Get response from MinIO server with one profiling object without errors
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
minioStartProfiling = func(_ madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return []madmin.StartProfilingResult{
|
||||
{
|
||||
NodeName: "http://127.0.0.1:9000/",
|
||||
@@ -71,7 +71,7 @@ func TestStartProfiling(t *testing.T) {
|
||||
return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil
|
||||
}
|
||||
// mock function response from mockConn.writeMessage()
|
||||
connWriteMessageMock = func(messageType int, p []byte) error {
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
err := startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
@@ -82,7 +82,7 @@ func TestStartProfiling(t *testing.T) {
|
||||
|
||||
// Test-2 : startProfiling() Correctly handles errors returned by MinIO
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
minioStartProfiling = func(_ madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
err = startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
@@ -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,11 +24,13 @@ import (
|
||||
"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/console/restapi/operations"
|
||||
release "github.com/minio/console/restapi/operations/release"
|
||||
"github.com/minio/pkg/env"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,13 +42,13 @@ 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(int(err.Code)).WithPayload(err)
|
||||
return release.NewListReleasesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return release.NewListReleasesOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetReleaseListResponse(_ *models.Principal, params release.ListReleasesParams) (*models.ReleaseListResponse, *models.Error) {
|
||||
func GetReleaseListResponse(_ *models.Principal, params release.ListReleasesParams) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
repo := params.Repo
|
||||
@@ -62,12 +64,14 @@ func GetReleaseListResponse(_ *models.Principal, params release.ListReleasesPara
|
||||
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, *models.Error) {
|
||||
func releaseList(ctx context.Context, repo, currentRelease, search, filter string) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
serviceURL := getReleaseServiceURL()
|
||||
releases, err := getReleases(serviceURL, repo, currentRelease, search, filter)
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
releases, err := getReleases(serviceURL, repo, currentRelease, search, filter, clientIP)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -79,7 +83,7 @@ func getReleaseServiceURL() string {
|
||||
return fmt.Sprintf("%s/releases", host)
|
||||
}
|
||||
|
||||
func getReleases(endpoint, repo, currentRelease, search, filter string) (*models.ReleaseListResponse, error) {
|
||||
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 {
|
||||
@@ -93,7 +97,7 @@ func getReleases(endpoint, repo, currentRelease, search, filter string) (*models
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := GetConsoleHTTPClient("")
|
||||
client := GetConsoleHTTPClient(clientIP)
|
||||
client.Timeout = time.Second * 5
|
||||
|
||||
resp, err := client.Do(req)
|
||||
@@ -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/json"
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
release "github.com/minio/console/api/operations/release"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
release "github.com/minio/console/restapi/operations/release"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -14,14 +14,14 @@
|
||||
// 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"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -32,10 +32,10 @@ func TestServiceRestart(t *testing.T) {
|
||||
function := "serviceRestart()"
|
||||
// Test-1 : serviceRestart() restart services no errors
|
||||
// mock function response from listGroups()
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
@@ -44,10 +44,10 @@ func TestServiceRestart(t *testing.T) {
|
||||
|
||||
// Test-2 : serviceRestart() returns errors on client.serviceRestart call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
MinioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); assert.Error(err) {
|
||||
@@ -56,10 +56,10 @@ func TestServiceRestart(t *testing.T) {
|
||||
|
||||
// Test-3 : serviceRestart() returns errors on client.serverInfo() call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, errors.New("error on server info")
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); 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"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
@@ -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,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v2"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -41,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) {
|
||||
@@ -59,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)
|
||||
@@ -84,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) {
|
||||
@@ -93,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) {
|
||||
@@ -110,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,8 +24,8 @@ 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"
|
||||
)
|
||||
|
||||
@@ -102,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
|
||||
@@ -135,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")
|
||||
}
|
||||
|
||||
@@ -150,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")
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ func TestRemoveUser(t *testing.T) {
|
||||
|
||||
// Test-1: removeUser() delete a user
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioRemoveUserMock = func(accessKey string) error {
|
||||
minioRemoveUserMock = func(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,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")
|
||||
}
|
||||
|
||||
@@ -220,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
|
||||
}
|
||||
|
||||
@@ -235,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")
|
||||
}
|
||||
|
||||
@@ -244,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
|
||||
}
|
||||
|
||||
@@ -279,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()"
|
||||
@@ -294,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)
|
||||
@@ -313,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 {
|
||||
@@ -321,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 {
|
||||
@@ -329,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) {
|
||||
@@ -337,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) {
|
||||
@@ -358,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
|
||||
}
|
||||
|
||||
@@ -368,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")
|
||||
}
|
||||
|
||||
@@ -527,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)
|
||||
})
|
||||
@@ -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,45 +24,22 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg"
|
||||
"github.com/minio/madmin-go/v2"
|
||||
mcCmd "github.com/minio/mc/cmd"
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
|
||||
"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/iam/policy"
|
||||
iampolicy "github.com/minio/pkg/v3/policy"
|
||||
)
|
||||
|
||||
const globalAppName = "MinIO Console"
|
||||
|
||||
// NewAdminClientWithInsecure gives a new madmin client interface either secure or insecure based on parameter
|
||||
func NewAdminClientWithInsecure(url, accessKey, secretKey, sessionToken string, insecure bool) (*madmin.AdminClient, *probe.Error) {
|
||||
admClient, err := s3AdminNew(&mcCmd.Config{
|
||||
HostURL: url,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
SessionToken: sessionToken,
|
||||
AppName: globalAppName,
|
||||
AppVersion: pkg.Version,
|
||||
Insecure: insecure,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err.Trace(url)
|
||||
}
|
||||
stsClient := PrepareConsoleHTTPClient(insecure)
|
||||
admClient.SetCustomTransport(stsClient.Transport)
|
||||
// set user-agent to differentiate Console UI requests for auditing.
|
||||
admClient.SetAppInfo("MinIO Console", pkg.Version)
|
||||
return admClient, nil
|
||||
}
|
||||
|
||||
// s3AdminNew returns an initialized minioAdmin structure. If debug is enabled,
|
||||
// it also enables an internal trace transport.
|
||||
var s3AdminNew = mcCmd.NewAdminFactory()
|
||||
|
||||
// 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.
|
||||
@@ -86,7 +63,6 @@ type MinioAdmin interface {
|
||||
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) ([]madmin.StartProfilingResult, error)
|
||||
@@ -97,7 +73,7 @@ type MinioAdmin interface {
|
||||
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 *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error)
|
||||
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)
|
||||
@@ -109,7 +85,7 @@ type MinioAdmin interface {
|
||||
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, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error)
|
||||
serverHealthInfo(ctx context.Context, deadline time.Duration) (interface{}, string, error)
|
||||
// List Tiers
|
||||
listTiers(ctx context.Context) ([]*madmin.TierConfig, error)
|
||||
// Tier Info
|
||||
@@ -120,12 +96,14 @@ type MinioAdmin interface {
|
||||
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) (*madmin.ReplicateAddStatus, error)
|
||||
editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo) (*madmin.ReplicateEditStatus, 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
|
||||
@@ -137,20 +115,8 @@ type MinioAdmin interface {
|
||||
kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error)
|
||||
kmsVersion(ctx context.Context) (*madmin.KMSVersion, error)
|
||||
createKey(ctx context.Context, key string) error
|
||||
importKey(ctx context.Context, key string, content []byte) error
|
||||
listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error)
|
||||
keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error)
|
||||
deleteKey(ctx context.Context, key string) error
|
||||
setKMSPolicy(ctx context.Context, policy string, content []byte) error
|
||||
assignPolicy(ctx context.Context, policy string, content []byte) error
|
||||
describePolicy(ctx context.Context, policy string) (*madmin.KMSDescribePolicy, error)
|
||||
getKMSPolicy(ctx context.Context, policy string) (*madmin.KMSPolicy, error)
|
||||
listKMSPolicies(ctx context.Context, pattern string) ([]madmin.KMSPolicyInfo, error)
|
||||
deletePolicy(ctx context.Context, policy string) error
|
||||
describeIdentity(ctx context.Context, identity string) (*madmin.KMSDescribeIdentity, error)
|
||||
describeSelfIdentity(ctx context.Context) (*madmin.KMSDescribeSelfIdentity, error)
|
||||
deleteIdentity(ctx context.Context, identity string) error
|
||||
listIdentities(ctx context.Context, pattern string) ([]madmin.KMSIdentityInfo, error)
|
||||
|
||||
// IDP
|
||||
addOrUpdateIDPConfig(ctx context.Context, idpType, cfgName, cfgData string, update bool) (restart bool, err error)
|
||||
@@ -238,11 +204,11 @@ func (ac AdminClient) listPolicies(ctx context.Context) (map[string]*iampolicy.P
|
||||
|
||||
// implements madmin.ListCannedPolicies()
|
||||
func (ac AdminClient) getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error) {
|
||||
praw, err := ac.Client.InfoCannedPolicy(ctx, name)
|
||||
info, err := ac.Client.InfoCannedPolicyV2(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iampolicy.ParseConfig(bytes.NewReader(praw))
|
||||
return iampolicy.ParseConfig(bytes.NewReader(info.Policy))
|
||||
}
|
||||
|
||||
// implements madmin.RemoveCannedPolicy()
|
||||
@@ -261,6 +227,7 @@ func (ac AdminClient) addPolicy(ctx context.Context, name string, policy *iampol
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -332,22 +299,21 @@ func (ac AdminClient) getLogs(ctx context.Context, node string, lineCnt int, log
|
||||
}
|
||||
|
||||
// implements madmin.AddServiceAccount()
|
||||
func (ac AdminClient) addServiceAccount(ctx context.Context, policy *iampolicy.Policy, user string, accessKey string, secretKey string) (madmin.Credentials, error) {
|
||||
buf, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return madmin.Credentials{}, err
|
||||
}
|
||||
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: buf,
|
||||
TargetUser: user,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
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) {
|
||||
// TODO: Fix this
|
||||
return ac.Client.ListServiceAccounts(ctx, user)
|
||||
}
|
||||
|
||||
@@ -413,44 +379,26 @@ func (ac AdminClient) getBucketQuota(ctx context.Context, bucket string) (madmin
|
||||
}
|
||||
|
||||
// serverHealthInfo implements mc.ServerHealthInfo - Connect to a minio server and call Health Info Management API
|
||||
func (ac AdminClient) serverHealthInfo(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error) {
|
||||
resp, version, err := ac.Client.ServerHealthInfo(ctx, healthDataTypes, deadline)
|
||||
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
|
||||
}
|
||||
|
||||
var healthInfo interface{}
|
||||
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
switch version {
|
||||
case madmin.HealthInfoVersion0:
|
||||
info := madmin.HealthInfoV0{}
|
||||
for {
|
||||
if err = decoder.Decode(&info); err != nil {
|
||||
break
|
||||
}
|
||||
for {
|
||||
if err = decoder.Decode(&info); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Old minio versions don't return the MinIO info in
|
||||
// response of the healthinfo api. So fetch it separately
|
||||
minioInfo, err := ac.Client.ServerInfo(ctx)
|
||||
if err != nil {
|
||||
info.Minio.Error = err.Error()
|
||||
} else {
|
||||
info.Minio.Info = minioInfo
|
||||
}
|
||||
|
||||
healthInfo = mcCmd.MapHealthInfoToV1(info, nil)
|
||||
version = madmin.HealthInfoVersion1
|
||||
case madmin.HealthInfoVersion:
|
||||
info := madmin.HealthInfo{}
|
||||
for {
|
||||
if err = decoder.Decode(&info); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
healthInfo = info
|
||||
}
|
||||
if info.Version == "" {
|
||||
return nil, "", ErrHealthReportFail
|
||||
}
|
||||
healthInfo = info
|
||||
|
||||
return healthInfo, version, nil
|
||||
}
|
||||
@@ -485,16 +433,23 @@ func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) err
|
||||
return ac.Client.VerifyTier(ctx, tierName)
|
||||
}
|
||||
|
||||
func NewMinioAdminClient(sessionClaims *models.Principal) (*madmin.AdminClient, error) {
|
||||
adminClient, err := newAdminFromClaims(sessionClaims)
|
||||
// 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) (*madmin.AdminClient, error) {
|
||||
func newAdminFromClaims(claims *models.Principal, clientIP string) (*madmin.AdminClient, error) {
|
||||
tlsEnabled := getMinIOEndpointIsSecure()
|
||||
endpoint := getMinIOEndpoint()
|
||||
|
||||
@@ -505,7 +460,8 @@ func newAdminFromClaims(claims *models.Principal) (*madmin.AdminClient, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminClient.SetCustomTransport(GetConsoleHTTPClient(getMinIOServer()).Transport)
|
||||
adminClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
adminClient.SetCustomTransport(PrepareSTSClientTransport(clientIP))
|
||||
return adminClient, nil
|
||||
}
|
||||
|
||||
@@ -518,23 +474,14 @@ func newAdminFromCreds(accessKey, secretKey, endpoint string, tlsEnabled bool) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minioClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
return minioClient, nil
|
||||
}
|
||||
|
||||
// httpClient is a custom http client, this client should not be called directly and instead be
|
||||
// called using GetConsoleHTTPClient() to ensure is initialized and the certificates are loaded correctly
|
||||
var httpClients = struct {
|
||||
sync.Mutex
|
||||
m map[string]*http.Client
|
||||
}{
|
||||
m: make(map[string]*http.Client),
|
||||
}
|
||||
|
||||
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||
// that points to the local machine - FQDN are not supported
|
||||
func isLocalIPEndpoint(addr string) bool {
|
||||
u, err := url.Parse(addr)
|
||||
func isLocalIPEndpoint(endpoint string) bool {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -547,6 +494,9 @@ func isLocalIPAddress(ipAddr string) bool {
|
||||
if ipAddr == "" {
|
||||
return false
|
||||
}
|
||||
if ipAddr == "localhost" {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(ipAddr)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
@@ -554,24 +504,75 @@ func isLocalIPAddress(ipAddr string) bool {
|
||||
// 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(address string) *http.Client {
|
||||
u, err := url.Parse(address)
|
||||
if err == nil {
|
||||
address = u.Hostname()
|
||||
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], `"`)
|
||||
}
|
||||
}
|
||||
|
||||
httpClients.Lock()
|
||||
client, ok := httpClients.m[address]
|
||||
httpClients.Unlock()
|
||||
if ok {
|
||||
return client
|
||||
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
|
||||
}
|
||||
|
||||
client = PrepareConsoleHTTPClient(isLocalIPAddress(address))
|
||||
httpClients.Lock()
|
||||
httpClients.m[address] = client
|
||||
httpClients.Unlock()
|
||||
return client
|
||||
// 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) {
|
||||
@@ -592,8 +593,8 @@ func (ac AdminClient) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteR
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite) (*madmin.ReplicateAddStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationAdd(ctx, sites)
|
||||
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
|
||||
}
|
||||
@@ -606,8 +607,8 @@ func (ac AdminClient) addSiteReplicationInfo(ctx context.Context, sites []madmin
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo) (*madmin.ReplicateEditStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationEdit(ctx, site)
|
||||
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
|
||||
}
|
||||
@@ -657,10 +658,6 @@ func (ac AdminClient) createKey(ctx context.Context, key string) error {
|
||||
return ac.Client.CreateKey(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) importKey(ctx context.Context, key string, content []byte) error {
|
||||
return ac.Client.ImportKey(ctx, key, content)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listKeys(ctx context.Context, pattern string) ([]madmin.KMSKeyInfo, error) {
|
||||
return ac.Client.ListKeys(ctx, pattern)
|
||||
}
|
||||
@@ -669,50 +666,6 @@ func (ac AdminClient) keyStatus(ctx context.Context, key string) (*madmin.KMSKey
|
||||
return ac.Client.GetKeyStatus(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteKey(ctx context.Context, key string) error {
|
||||
return ac.Client.DeleteKey(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) setKMSPolicy(ctx context.Context, policy string, content []byte) error {
|
||||
return ac.Client.SetKMSPolicy(ctx, policy, content)
|
||||
}
|
||||
|
||||
func (ac AdminClient) assignPolicy(ctx context.Context, policy string, content []byte) error {
|
||||
return ac.Client.AssignPolicy(ctx, policy, content)
|
||||
}
|
||||
|
||||
func (ac AdminClient) describePolicy(ctx context.Context, policy string) (*madmin.KMSDescribePolicy, error) {
|
||||
return ac.Client.DescribePolicy(ctx, policy)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getKMSPolicy(ctx context.Context, policy string) (*madmin.KMSPolicy, error) {
|
||||
return ac.Client.GetPolicy(ctx, policy)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listKMSPolicies(ctx context.Context, pattern string) ([]madmin.KMSPolicyInfo, error) {
|
||||
return ac.Client.ListPolicies(ctx, pattern)
|
||||
}
|
||||
|
||||
func (ac AdminClient) deletePolicy(ctx context.Context, policy string) error {
|
||||
return ac.Client.DeletePolicy(ctx, policy)
|
||||
}
|
||||
|
||||
func (ac AdminClient) describeIdentity(ctx context.Context, identity string) (*madmin.KMSDescribeIdentity, error) {
|
||||
return ac.Client.DescribeIdentity(ctx, identity)
|
||||
}
|
||||
|
||||
func (ac AdminClient) describeSelfIdentity(ctx context.Context) (*madmin.KMSDescribeSelfIdentity, error) {
|
||||
return ac.Client.DescribeSelfIdentity(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteIdentity(ctx context.Context, identity string) error {
|
||||
return ac.Client.DeleteIdentity(ctx, identity)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listIdentities(ctx context.Context, pattern string) ([]madmin.KMSIdentityInfo, error) {
|
||||
return ac.Client.ListIdentities(ctx, pattern)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -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"
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
"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 +38,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 +73,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 +206,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 +221,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 +254,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 +267,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) {
|
||||
@@ -329,7 +319,7 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
|
||||
return s.stsAssumeRole.IsExpired()
|
||||
}
|
||||
|
||||
func stsCredentials(minioURL, accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
||||
func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) {
|
||||
if accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("credentials endpoint, access and secret key are mandatory for AssumeRoleSTS")
|
||||
}
|
||||
@@ -340,7 +330,7 @@ func stsCredentials(minioURL, accessKey, secretKey, location string) (*credentia
|
||||
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
||||
}
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
Client: GetConsoleHTTPClient(minioURL),
|
||||
Client: GetConsoleHTTPClient(clientIP),
|
||||
STSEndpoint: minioURL,
|
||||
Options: opts,
|
||||
}
|
||||
@@ -348,7 +338,7 @@ func stsCredentials(minioURL, accessKey, secretKey, location string) (*credentia
|
||||
return credentials.New(consoleSTSWrapper), nil
|
||||
}
|
||||
|
||||
func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
||||
func NewConsoleCredentials(accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) {
|
||||
minioURL := getMinIOServer()
|
||||
|
||||
// Future authentication methods can be added under this switch statement
|
||||
@@ -356,7 +346,7 @@ func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
||||
// LDAP authentication for Console
|
||||
case ldap.GetLDAPEnabled():
|
||||
{
|
||||
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(minioURL), minioURL, accessKey, secretKey)
|
||||
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(clientIP), minioURL, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -366,7 +356,7 @@ func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
||||
|
||||
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)
|
||||
stsCreds, errSTS := stsCredentials(minioURL, accessKey, secretKey, location, clientIP)
|
||||
|
||||
// If there is an error with STS too, then we return the original LDAP error
|
||||
if errSTS != nil {
|
||||
@@ -390,7 +380,7 @@ func NewConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
||||
// default authentication for Console is via STS (Security Token Service) against MinIO
|
||||
default:
|
||||
{
|
||||
return stsCredentials(minioURL, accessKey, secretKey, location)
|
||||
return stsCredentials(minioURL, accessKey, secretKey, location, clientIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,14 +396,14 @@ func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Cre
|
||||
|
||||
// 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
|
||||
@@ -425,10 +415,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) != "" {
|
||||
@@ -437,11 +426,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")
|
||||
}
|
||||
@@ -450,7 +439,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
|
||||
@@ -472,27 +461,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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -14,18 +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 (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
xcerts "github.com/minio/pkg/certs"
|
||||
"github.com/minio/pkg/env"
|
||||
xnet "github.com/minio/pkg/net"
|
||||
xcerts "github.com/minio/pkg/v3/certs"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -54,6 +57,31 @@ var (
|
||||
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
|
||||
@@ -70,10 +98,6 @@ func getMinIOServer() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIOServer, "http://localhost:9000"))
|
||||
}
|
||||
|
||||
func getSubnetProxy() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleSubnetProxy, ""))
|
||||
}
|
||||
|
||||
func GetMinIORegion() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIORegion, ""))
|
||||
}
|
||||
@@ -213,11 +237,6 @@ func GetSecureForceSTSHeader() bool {
|
||||
return strings.ToLower(env.Get(ConsoleSecureForceSTSHeader, "off")) == "on"
|
||||
}
|
||||
|
||||
// PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
|
||||
func GetSecurePublicKey() string {
|
||||
return env.Get(ConsoleSecurePublicKey, "")
|
||||
}
|
||||
|
||||
// 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, "")
|
||||
@@ -228,10 +247,6 @@ func GetSecureFeaturePolicy() string {
|
||||
return env.Get(ConsoleSecureFeaturePolicy, "")
|
||||
}
|
||||
|
||||
func GetSecureExpectCTHeader() string {
|
||||
return env.Get(ConsoleSecureExpectCTHeader, "")
|
||||
}
|
||||
|
||||
func getLogSearchAPIToken() string {
|
||||
if v := env.Get(ConsoleLogQueryAuthToken, ""); v != "" {
|
||||
return v
|
||||
@@ -247,6 +262,10 @@ func getPrometheusURL() string {
|
||||
return env.Get(PrometheusURL, "")
|
||||
}
|
||||
|
||||
func getPrometheusAuthToken() string {
|
||||
return env.Get(PrometheusAuthToken, "")
|
||||
}
|
||||
|
||||
func getPrometheusJobID() string {
|
||||
return env.Get(PrometheusJobID, "minio-job")
|
||||
}
|
||||
@@ -276,3 +295,11 @@ func getMaxConcurrentDownloadsLimit() int64 {
|
||||
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, "")
|
||||
}
|
||||
@@ -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"
|
||||
@@ -54,7 +54,7 @@ func TestGetPort(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsolePort, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetPort(), "GetPort()")
|
||||
os.Unsetenv(ConsolePort)
|
||||
@@ -87,7 +87,7 @@ func TestGetTLSPort(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleTLSPort, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetTLSPort(), "GetTLSPort()")
|
||||
os.Unsetenv(ConsoleTLSPort)
|
||||
@@ -120,7 +120,7 @@ func TestGetSecureAllowedHosts(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureAllowedHosts, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureAllowedHosts(), "GetSecureAllowedHosts()")
|
||||
os.Unsetenv(ConsoleSecureAllowedHosts)
|
||||
@@ -153,7 +153,7 @@ func TestGetSecureHostsProxyHeaders(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureHostsProxyHeaders, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureHostsProxyHeaders(), "GetSecureHostsProxyHeaders()")
|
||||
os.Unsetenv(ConsoleSecureHostsProxyHeaders)
|
||||
@@ -186,7 +186,7 @@ func TestGetSecureSTSSeconds(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleSecureSTSSeconds, tt.args.env)
|
||||
assert.Equalf(t, tt.want, GetSecureSTSSeconds(), "GetSecureSTSSeconds()")
|
||||
os.Unsetenv(ConsoleSecureSTSSeconds)
|
||||
@@ -219,7 +219,7 @@ func Test_getLogSearchAPIToken(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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)
|
||||
@@ -252,7 +252,7 @@ func Test_getPrometheusURL(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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)
|
||||
@@ -285,7 +285,7 @@ func Test_getPrometheusJobID(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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)
|
||||
@@ -318,7 +318,7 @@ func Test_getMaxConcurrentUploadsLimit(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleMaxConcurrentUploads, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getMaxConcurrentUploadsLimit(), "getMaxConcurrentUploadsLimit()")
|
||||
os.Unsetenv(ConsoleMaxConcurrentUploads)
|
||||
@@ -351,7 +351,7 @@ func Test_getMaxConcurrentDownloadsLimit(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
os.Setenv(ConsoleMaxConcurrentDownloads, tt.args.env)
|
||||
assert.Equalf(t, tt.want, getMaxConcurrentDownloadsLimit(), "getMaxConcurrentDownloadsLimit()")
|
||||
os.Unsetenv(ConsoleMaxConcurrentDownloads)
|
||||
@@ -384,7 +384,7 @@ func Test_getConsoleDevMode(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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,7 +84,7 @@ 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" {
|
||||
@@ -101,7 +105,7 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
CustomStyleOb: claims.CustomStyleOB,
|
||||
}, nil
|
||||
}
|
||||
api.AnonymousAuth = func(s string) (*models.Principal, error) {
|
||||
api.AnonymousAuth = func(_ string) (*models.Principal, error) {
|
||||
return &models.Principal{}, nil
|
||||
}
|
||||
|
||||
@@ -121,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
|
||||
@@ -141,24 +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)
|
||||
// Register Support Handler
|
||||
registerSupportHandlers(api)
|
||||
|
||||
// Operator Console
|
||||
|
||||
// Register Object's Handlers
|
||||
@@ -170,13 +161,12 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
|
||||
registerReleasesHandlers(api)
|
||||
|
||||
registerPublicObjectsHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
api.ServerShutdown = func() {}
|
||||
|
||||
// do an initial subnet plan caching
|
||||
fetchLicensePlan()
|
||||
|
||||
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
|
||||
}
|
||||
|
||||
@@ -194,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))
|
||||
})
|
||||
}
|
||||
@@ -217,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 {
|
||||
@@ -229,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)
|
||||
@@ -257,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)
|
||||
@@ -316,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 {
|
||||
@@ -324,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(requestBounce(http.FileServer(http.FS(buildFs)))).ServeHTTP(w, r)
|
||||
spaFileHandler.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -427,13 +507,10 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -471,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 {
|
||||
@@ -483,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
|
||||
}
|
||||
@@ -510,8 +587,6 @@ func replaceBaseInIndex(indexPageBytes []byte, basePath string) []byte {
|
||||
|
||||
func replaceLicense(indexPageBytes []byte) []byte {
|
||||
indexPageStr := string(indexPageBytes)
|
||||
newPlan := fmt.Sprintf("<meta name=\"minio-license\" content=\"%s\" />", InstanceLicensePlan.String())
|
||||
indexPageStr = strings.Replace(indexPageStr, "<meta name=\"minio-license\" content=\"apgl\"/>", newPlan, 1)
|
||||
indexPageBytes = []byte(indexPageStr)
|
||||
return indexPageBytes
|
||||
}
|
||||
@@ -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{}
|
||||
@@ -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
|
||||
|
||||
// list of all console environment constants
|
||||
const (
|
||||
@@ -47,6 +47,7 @@ const (
|
||||
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"
|
||||
@@ -54,6 +55,9 @@ const (
|
||||
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)
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
// Package restapi MinIO Console Server
|
||||
// Package api MinIO Console Server
|
||||
//
|
||||
// Schemes:
|
||||
// http
|
||||
@@ -30,9 +30,8 @@
|
||||
// - multipart/form-data
|
||||
//
|
||||
// Produces:
|
||||
// - application/zip
|
||||
// - application/octet-stream
|
||||
// - application/json
|
||||
//
|
||||
// swagger:meta
|
||||
package restapi
|
||||
package api
|
||||
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")
|
||||
@@ -53,6 +53,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,17 +72,25 @@ var (
|
||||
ErrEncryptionConfigNotFound = errors.New("encryption configuration not found")
|
||||
ErrPolicyNotFound = errors.New("policy does not exist")
|
||||
ErrLoginNotAllowed = errors.New("login not allowed")
|
||||
ErrSubnetUploadFail = errors.New("Subnet upload failed")
|
||||
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 {
|
||||
@@ -99,15 +108,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()
|
||||
}
|
||||
@@ -201,6 +222,7 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorMessage = ErrAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err1).Code == "InvalidAccessKeyId" {
|
||||
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidSession.Error()
|
||||
}
|
||||
@@ -240,6 +262,7 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorCode = 400
|
||||
errorMessage = "Bucket already exists"
|
||||
}
|
||||
|
||||
LogError("ErrorWithContext:%v", err...)
|
||||
LogIf(ctx, err1, err...)
|
||||
}
|
||||
@@ -250,11 +273,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...)
|
||||
@@ -14,14 +14,13 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ func TestError(t *testing.T) {
|
||||
type testError struct {
|
||||
name string
|
||||
args args
|
||||
want *models.Error
|
||||
want *CodedAPIError
|
||||
}
|
||||
|
||||
var tests []testError
|
||||
@@ -45,37 +44,37 @@ func TestError(t *testing.T) {
|
||||
}
|
||||
|
||||
appErrors := map[string]expectedError{
|
||||
"ErrDefault": {code: 500, err: ErrDefault},
|
||||
"ErrInvalidLogin": {code: 401, err: ErrInvalidLogin},
|
||||
"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},
|
||||
"ErrBucketLifeCycleNotConfigured": {code: 404, err: ErrBucketLifeCycleNotConfigured},
|
||||
"ErrChangePassword": {code: 403, err: ErrChangePassword},
|
||||
"ErrInvalidLicense": {code: 404, err: ErrInvalidLicense},
|
||||
"ErrLicenseNotFound": {code: 404, err: ErrLicenseNotFound},
|
||||
"ErrAvoidSelfAccountDelete": {code: 403, err: ErrAvoidSelfAccountDelete},
|
||||
"ErrAccessDenied": {code: 403, err: ErrAccessDenied},
|
||||
"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},
|
||||
"ErrTooFewNodes": {code: 500, err: ErrTooFewNodes},
|
||||
"ErrAccessDenied": {code: 403, err: ErrAccessDenied},
|
||||
"ErrTooFewAvailableNodes": {code: 500, err: ErrTooFewAvailableNodes},
|
||||
"ErrFewerThanFourNodes": {code: 500, err: ErrFewerThanFourNodes},
|
||||
"ErrUnableToGetTenantLogs": {code: 500, err: ErrUnableToGetTenantLogs},
|
||||
@@ -91,22 +90,41 @@ func TestError(t *testing.T) {
|
||||
args: args{
|
||||
err: []interface{}{e.err},
|
||||
},
|
||||
want: &models.Error{Code: int32(e.code), Message: swag.String(e.err.Error()), DetailedMessage: swag.String(e.err.Error())},
|
||||
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: &models.Error{Code: int32(401), Message: swag.String(ErrDefault.Error()), DetailedMessage: swag.String(ErrDefault.Error())},
|
||||
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(t *testing.T) {
|
||||
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.DetailedMessage, *got.DetailedMessage, "Error(%s) Got (%s)", *tt.want.DetailedMessage, *got.DetailedMessage)
|
||||
assert.Equalf(t, tt.want.APIError.DetailedMessage, got.APIError.DetailedMessage, "Error(%s) Got (%s)", tt.want.APIError.DetailedMessage, got.APIError.DetailedMessage)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -119,7 +137,7 @@ func TestErrorWithContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *models.Error
|
||||
want *CodedAPIError
|
||||
}{
|
||||
{
|
||||
name: "default error",
|
||||
@@ -127,11 +145,13 @@ func TestErrorWithContext(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
err: []interface{}{ErrDefault},
|
||||
},
|
||||
want: &models.Error{Code: 500, Message: swag.String(ErrDefault.Error()), DetailedMessage: swag.String(ErrDefault.Error())},
|
||||
want: &CodedAPIError{
|
||||
Code: 500, APIError: &models.APIError{Message: ErrDefault.Error(), DetailedMessage: ErrDefault.Error()},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package restapi
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -58,7 +58,7 @@ type Context struct {
|
||||
TLSCertificate, TLSKey, TLSca string
|
||||
}
|
||||
|
||||
// Load loads restapi Context from command line context.
|
||||
// Load loads api Context from command line context.
|
||||
func (c *Context) Load(ctx *cli.Context) error {
|
||||
*c = Context{
|
||||
Host: ctx.String("host"),
|
||||
@@ -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 (
|
||||
"flag"
|
||||
@@ -85,7 +85,7 @@ func TestContext_Load(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
c := &Context{}
|
||||
|
||||
fs := flag.NewFlagSet("flags", flag.ContinueOnError)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ type LoginDetailDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginDetailDefault creates LoginDetailDefault with default headers values
|
||||
@@ -112,13 +112,13 @@ func (o *LoginDetailDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login detail default response
|
||||
func (o *LoginDetailDefault) WithPayload(payload *models.Error) *LoginDetailDefault {
|
||||
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.Error) {
|
||||
func (o *LoginDetailDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ type LoginOauth2AuthDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginOauth2AuthDefault creates LoginOauth2AuthDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *LoginOauth2AuthDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login oauth2 auth default response
|
||||
func (o *LoginOauth2AuthDefault) WithPayload(payload *models.Error) *LoginOauth2AuthDefault {
|
||||
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.Error) {
|
||||
func (o *LoginOauth2AuthDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ type LogoutDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLogoutDefault creates LogoutDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *LogoutDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the logout default response
|
||||
func (o *LogoutDefault) WithPayload(payload *models.Error) *LogoutDefault {
|
||||
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.Error) {
|
||||
func (o *LogoutDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ type SessionCheckDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewSessionCheckDefault creates SessionCheckDefault with default headers values
|
||||
@@ -112,13 +112,13 @@ func (o *SessionCheckDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the session check default response
|
||||
func (o *SessionCheckDefault) WithPayload(payload *models.Error) *SessionCheckDefault {
|
||||
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.Error) {
|
||||
func (o *SessionCheckDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ type AddRemoteBucketDefault struct {
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAddRemoteBucketDefault creates AddRemoteBucketDefault with default headers values
|
||||
@@ -92,13 +92,13 @@ func (o *AddRemoteBucketDefault) SetStatusCode(code int) {
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the add remote bucket default response
|
||||
func (o *AddRemoteBucketDefault) WithPayload(payload *models.Error) *AddRemoteBucketDefault {
|
||||
func (o *AddRemoteBucketDefault) WithPayload(payload *models.APIError) *AddRemoteBucketDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the add remote bucket default response
|
||||
func (o *AddRemoteBucketDefault) SetPayload(payload *models.Error) {
|
||||
func (o *AddRemoteBucketDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user