Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee974a5961 | ||
|
|
40c4ce76c5 | ||
|
|
28b080122a | ||
|
|
6df2db82af | ||
|
|
ff2b72f869 | ||
|
|
f5ad4defc9 | ||
|
|
6499469487 | ||
|
|
71b1b708b7 | ||
|
|
63c6d8952b | ||
|
|
33a7fbb205 | ||
|
|
36d0e78834 | ||
|
|
9e0416f1ab | ||
|
|
f4a08fc0af | ||
|
|
115de8ee25 | ||
|
|
27cb17dbfd | ||
|
|
31a5d640eb | ||
|
|
b45e11ce7f | ||
|
|
4388ecc4eb | ||
|
|
656d8075b2 | ||
|
|
8b209a202f | ||
|
|
2e66a13f48 | ||
|
|
d54ed26c71 | ||
|
|
5cf02ffae9 | ||
|
|
6afd0b16dc | ||
|
|
0b8af385f1 | ||
|
|
9a3063b7da | ||
|
|
71541029e7 | ||
|
|
076b803a5c | ||
|
|
97ef82f831 | ||
|
|
2ca484c691 | ||
|
|
4e5dcf0fc3 | ||
|
|
18e50975d4 | ||
|
|
d425af3c85 | ||
|
|
fce84d1de0 | ||
|
|
dc19984f23 | ||
|
|
8c1ecae68a | ||
|
|
862d692444 | ||
|
|
854a0c16b3 | ||
|
|
3d74e9762c | ||
|
|
3d8b98f563 | ||
|
|
06af416642 | ||
|
|
01920841d2 | ||
|
|
78aceb2b53 | ||
|
|
dce9bbd046 | ||
|
|
9f4573ade8 | ||
|
|
24af63da42 | ||
|
|
52c77fd388 | ||
|
|
19e6cc87c2 | ||
|
|
9bfed73b03 | ||
|
|
095241517b | ||
|
|
339885b2a0 | ||
|
|
6cfc985337 | ||
|
|
1c47685aea | ||
|
|
efa74a5f50 | ||
|
|
4c432cd356 | ||
|
|
0b07cb3885 | ||
|
|
8d13be5e87 | ||
|
|
3c34602f9e | ||
|
|
adfc96074f | ||
|
|
7cc7b874d1 | ||
|
|
4d12a5061d | ||
|
|
b274add4da | ||
|
|
fd51c9dc4c | ||
|
|
51f8794aa6 | ||
|
|
3db998f9c4 | ||
|
|
0a2a7087a7 | ||
|
|
8f0eb11ced | ||
|
|
e3e3599095 | ||
|
|
aa74e31453 | ||
|
|
e1f6e729fd | ||
|
|
13d83a6d1c | ||
|
|
6075387654 | ||
|
|
69fad3f55f | ||
|
|
e3864b62a4 | ||
|
|
22176f4e0f | ||
|
|
a89d7ec0ea | ||
|
|
8262049e20 | ||
|
|
c61e1e0a2a | ||
|
|
b376cf6c65 | ||
|
|
16bae25ce6 | ||
|
|
57ba17a12e | ||
|
|
f4d98a4910 | ||
|
|
fa32d78ff1 | ||
|
|
56f22a4479 | ||
|
|
7b88d3a1bc | ||
|
|
c8a39f9544 | ||
|
|
e77d1be53e | ||
|
|
6765bd0624 | ||
|
|
5f7b563a01 | ||
|
|
3885875149 | ||
|
|
cf05d5026f | ||
|
|
3e83a30739 | ||
|
|
49c5f5a8f0 | ||
|
|
72939e0cd7 | ||
|
|
271560894a | ||
|
|
6a591c1bcd | ||
|
|
3fdcfef1b4 | ||
|
|
7b8cfa2062 | ||
|
|
cc5921fd74 | ||
|
|
d027b7f759 | ||
|
|
f1524b0120 | ||
|
|
9985892751 | ||
|
|
cfd60bdd91 | ||
|
|
779f2a86e5 | ||
|
|
f47c4445bd | ||
|
|
1aeb4cc3d5 | ||
|
|
9e0a0205cc | ||
|
|
1058efb17a | ||
|
|
d0f744ebef | ||
|
|
a8c043cb16 | ||
|
|
978e02b5dc | ||
|
|
b39dbfff96 |
@@ -1,7 +0,0 @@
|
||||
node_modules/
|
||||
dist/
|
||||
target/
|
||||
console
|
||||
!console/
|
||||
web-app/node_modules/
|
||||
.git/
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -8,7 +8,8 @@ assignees: ''
|
||||
---
|
||||
|
||||
## NOTE
|
||||
If this case is urgent, please subscribe to [Subnet](https://min.io/pricing) so that our 24/7 support team may help you faster.
|
||||
|
||||
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 -->
|
||||
|
||||
|
||||
625
.github/workflows/jobs.yaml
vendored
625
.github/workflows/jobs.yaml
vendored
File diff suppressed because it is too large
Load Diff
24
.github/workflows/vulncheck.yaml
vendored
24
.github/workflows/vulncheck.yaml
vendored
@@ -5,9 +5,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
@@ -18,11 +15,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.9
|
||||
go-version: 1.23.8
|
||||
check-latest: true
|
||||
- name: Get official govulncheck
|
||||
run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
@@ -36,18 +33,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.21.8 ]
|
||||
go-version: [ 1.23.x ]
|
||||
os: [ ubuntu-latest ]
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
uses: actions/checkout@v4
|
||||
- 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 }}
|
||||
cache: "yarn"
|
||||
cache-dependency-path: web-app/yarn.lock
|
||||
- name: Checks for known security issues with the installed packages
|
||||
working-directory: ./web-app
|
||||
continue-on-error: false
|
||||
run: |
|
||||
yarn audit --groups dependencies
|
||||
yarn npm audit --recursive --environment production --no-deprecations
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -48,3 +48,6 @@ public.crt
|
||||
*.code-workspace
|
||||
*~
|
||||
.eslintcache
|
||||
|
||||
# Ignore Bin files
|
||||
bin/
|
||||
|
||||
55
.golangci.bck.yml
Normal file
55
.golangci.bck.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
linters-settings:
|
||||
misspell:
|
||||
locale: US
|
||||
testifylint:
|
||||
disable:
|
||||
- go-require
|
||||
staticcheck:
|
||||
checks:
|
||||
[
|
||||
"all",
|
||||
"-ST1005",
|
||||
"-ST1000",
|
||||
"-SA4000",
|
||||
"-SA9004",
|
||||
"-SA1019",
|
||||
"-SA1008",
|
||||
"-U1000",
|
||||
"-ST1016",
|
||||
]
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
copyright-holder: Copyright \(c\) (20\d\d\-20\d\d)|2021|({{year}})
|
||||
template-path: .license.tmpl
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- goimports
|
||||
- misspell
|
||||
- govet
|
||||
- revive
|
||||
- ineffassign
|
||||
- gosimple
|
||||
- gomodguard
|
||||
- gofmt
|
||||
- unused
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- durationcheck
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
- should have a package comment
|
||||
# TODO(y4m4): Remove once all exported ident. have comments!
|
||||
- comment on exported function
|
||||
- comment on exported type
|
||||
- should have comment
|
||||
- use leading k in Go names
|
||||
- comment on exported const
|
||||
exclude-dirs:
|
||||
- api/operations
|
||||
110
.golangci.yml
110
.golangci.yml
@@ -1,49 +1,73 @@
|
||||
linters-settings:
|
||||
golint:
|
||||
min-confidence: 0
|
||||
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
copyright-holder: Copyright \(c\) (20\d\d\-20\d\d)|2021|({{year}})
|
||||
template-path: .license.tmpl
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- goimports
|
||||
- misspell
|
||||
- govet
|
||||
- revive
|
||||
- ineffassign
|
||||
- gosimple
|
||||
- durationcheck
|
||||
- gocritic
|
||||
- gomodguard
|
||||
- gofmt
|
||||
- unused
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- revive
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- gocritic
|
||||
- unused
|
||||
settings:
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
copyright-holder: Copyright \(c\) (20\d\d\-20\d\d)|2021|({{year}})
|
||||
template-path: .license.tmpl
|
||||
misspell:
|
||||
locale: US
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -QF1001
|
||||
- -QF1008
|
||||
- -QF1010
|
||||
- -QF1012
|
||||
- -SA1008
|
||||
- -SA1019
|
||||
- -SA4000
|
||||
- -SA9004
|
||||
- -ST1000
|
||||
- -ST1005
|
||||
- -ST1016
|
||||
- -ST1019
|
||||
- -U1000
|
||||
testifylint:
|
||||
disable:
|
||||
- go-require
|
||||
exclusions:
|
||||
generated: lax
|
||||
rules:
|
||||
- path: (.+)\.go$
|
||||
text: should have a package comment
|
||||
- path: (.+)\.go$
|
||||
text: comment on exported function
|
||||
- path: (.+)\.go$
|
||||
text: comment on exported type
|
||||
- path: (.+)\.go$
|
||||
text: should have comment
|
||||
- path: (.+)\.go$
|
||||
text: use leading k in Go names
|
||||
- path: (.+)\.go$
|
||||
text: comment on exported const
|
||||
paths:
|
||||
- api/operations
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- durationcheck
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.43.0 # use the fixed version to not introduce new linters unexpectedly
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
- should have a package comment
|
||||
# TODO(y4m4): Remove once all exported ident. have comments!
|
||||
- comment on exported function
|
||||
- comment on exported type
|
||||
- should have comment
|
||||
- use leading k in Go names
|
||||
- comment on exported const
|
||||
run:
|
||||
skip-dirs:
|
||||
- pkg/clientgen
|
||||
- pkg/apis/networking.gke.io
|
||||
- api/operations
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- api/operations
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
150
CHANGELOG.md
150
CHANGELOG.md
@@ -1,7 +1,153 @@
|
||||
<!-- @format -->
|
||||
|
||||
# Changelog
|
||||
|
||||
## Release v2.0.0
|
||||
|
||||
Community version is going back to be an object browser only.
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed Dependencies vulnerabilities
|
||||
|
||||
Deprecations:
|
||||
|
||||
- Deprecated support of accounts & policies management, this can be managed by using mc admin commands. Please refer to the [MinIO Console User Management page](https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management/minio-user-management.html#id1) for more information.
|
||||
- Deprecated support of bucket management, this can be managed by using mc commands. Please refer to the [MinIO Client](https://min.io/docs/minio/linux/reference/minio-mc.html) for more information.
|
||||
- Deprecated support of configuration management, this can be managed by using mc admin config commands. Please refer to the [MinIO Client](https://min.io/docs/minio/linux/reference/minio-mc.html) for more information.
|
||||
|
||||
|
||||
## Release v1.7.6
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fix null pointer exception in Admin Info
|
||||
- Ignore leading or trailing spaces in login request
|
||||
- Fix file path on drag and drop
|
||||
- Fix typo in User DN Search Filter example
|
||||
|
||||
## Release v1.7.5
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed leaks during ZIP multiobject downloads
|
||||
- Allow spaces in Policy names
|
||||
|
||||
## Release v1.7.4
|
||||
|
||||
Deprecations:
|
||||
|
||||
- Deprecated support tools User Interface in favor of mc admin commands. Please refer to the [MinIO SUBNET Registration page](https://min.io/docs/minio/linux/administration/console/subnet-registration.html#subnet) for more information.
|
||||
- Deprecated Site replication User Interface in favor of mc admin commands. Please refer to the [MinIO Site Replication page](https://min.io/docs/minio/linux/operations/install-deploy-manage/multi-site-replication.html) for more information.
|
||||
- Deprecated Lifecycle & Tiers User Interface in favor of mc admin commands. Please refer to the [MinIO Tiers page](https://min.io/docs/minio/linux/reference/minio-mc/mc-ilm-tier.html) for more information.
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Avoid loading unpkg.com call when login animation is off
|
||||
|
||||
## Release v1.7.3
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Use a fixed public license verification key
|
||||
- Show non-expiring access keys as `no-expiry` instead of Jan 1, 1970
|
||||
- Use "join Slack" button for non-commercial edition instead of "Signup"
|
||||
- Fix setting policies on groups that have spaces
|
||||
|
||||
## Release v1.7.2
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed issue in Server Health Info
|
||||
- Fixed Security vulnerability in dependencies
|
||||
- Fixed client string in trace message
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Remove live logs in Call Home Page
|
||||
- Update License page
|
||||
|
||||
## Release v1.7.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed issue that could cause a failure when attempting to view deleted files in the object browser
|
||||
- Return network error when logging in and the network connection fails
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Added debug logging for console HTTP request (see [PR #3440](https://github.com/minio/console/pull/3440) for more detailed information)
|
||||
|
||||
## Release v1.7.0
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed directory listing
|
||||
- Fix MinIO videos link
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Removed deprecated KES functionality
|
||||
|
||||
## Release v1.6.3
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Updated go.mod version
|
||||
|
||||
## Release v1.6.2
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed minor user session issues
|
||||
- Updated project dependencies
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Improved Drives List visualization
|
||||
- Improved WS request logic
|
||||
- Updated License page with current MinIO plans.
|
||||
|
||||
## Release v1.6.1
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed objectManager issues under certain conditions
|
||||
- Fixed Security vulnerability in dependencies
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Improved Share Link behavior
|
||||
|
||||
## Release v1.6.0
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed share link encoding
|
||||
- Fixed Edit Lifecycle Storage Class
|
||||
- Added Tiers Improvements for Bucket Lifecycle management
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Vulnerability updates
|
||||
- Update Logo logic
|
||||
|
||||
## Release v1.5.0
|
||||
|
||||
Features:
|
||||
|
||||
- Added remove Tier functionality
|
||||
|
||||
Bug Fix:
|
||||
|
||||
- Fixed ILM rule tags not being shown
|
||||
- Fixed race condition Object Browser websocket
|
||||
- Fixed Encryption page crashing on empty response
|
||||
- Fixed Replication Delete Marker comparisons
|
||||
|
||||
Additional Changes:
|
||||
|
||||
- Use automatic URI encoding for APIs
|
||||
- Vulnerability updates
|
||||
|
||||
## Release v1.4.0
|
||||
|
||||
Features:
|
||||
|
||||
@@ -77,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 ./web-app/package.json ./
|
||||
COPY ./web-app/yarn.lock ./
|
||||
RUN yarn install
|
||||
|
||||
COPY ./web-app .
|
||||
|
||||
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/web-app/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 ./web-app/package.json ./
|
||||
COPY ./web-app/yarn.lock ./
|
||||
RUN yarn install
|
||||
|
||||
COPY ./web-app .
|
||||
|
||||
RUN yarn install && make build-static
|
||||
|
||||
USER node
|
||||
@@ -1,27 +0,0 @@
|
||||
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.2 as build
|
||||
|
||||
RUN microdnf update --nodocs && microdnf install ca-certificates --nodocs
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro:9.2
|
||||
|
||||
ARG TAG
|
||||
|
||||
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."
|
||||
|
||||
# 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 LICENSE /LICENSE
|
||||
COPY CREDITS /CREDITS
|
||||
COPY console /console
|
||||
|
||||
EXPOSE 9090
|
||||
|
||||
ENTRYPOINT ["/console"]
|
||||
20
Makefile
20
Makefile
@@ -57,14 +57,18 @@ clean-swagger:
|
||||
swagger-console:
|
||||
@echo "Generating swagger server code from yaml"
|
||||
@swagger generate server -A console --main-package=management --server-package=api --exclude-main -P models.Principal -f ./swagger.yml -r NOTICE
|
||||
@echo "Ensure basic install"
|
||||
@(cd web-app; yarn; cd ..)
|
||||
@echo "Generating typescript api"
|
||||
@npx swagger-typescript-api -p ./swagger.yml -o ./web-app/src/api -n consoleApi.ts
|
||||
@make swagger-typescript-api path="../swagger.yml" output="./src/api" name="consoleApi.ts"
|
||||
@git restore api/server.go
|
||||
|
||||
swagger-typescript-api:
|
||||
@(cd web-app; yarn swagger-typescript-api -p $(path) -o $(output) -n $(name) --custom-config ../generator.config.js; cd ..)
|
||||
|
||||
assets:
|
||||
@(if [ -f "${NVM_DIR}/nvm.sh" ]; then \. "${NVM_DIR}/nvm.sh" && nvm install && nvm use && npm install -g yarn ; fi &&\
|
||||
cd web-app; yarn install --prefer-offline; make build-static; yarn prettier --write . --loglevel warn; cd ..)
|
||||
cd web-app; corepack enable; yarn install --prefer-offline; make build-static; yarn prettier --write . --log-level warn; cd ..)
|
||||
|
||||
test-integration:
|
||||
@(docker stop pgsqlcontainer || true)
|
||||
@@ -206,21 +210,11 @@ test-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)/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)/web-app/tests/scripts/permissions.sh "web-app/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)/web-app/tests/scripts/permissions.sh "web-app/tests/permissions-7/")
|
||||
@(docker stop minio)
|
||||
|
||||
test-apply-permissions:
|
||||
@(env bash $(PWD)/web-app/tests/scripts/initialize-env.sh)
|
||||
|
||||
@@ -243,7 +237,7 @@ test-start-docker-minio-w-redirect-url: initialize-docker-network
|
||||
-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})
|
||||
quay.io/minio/minio:latest server /data{1...4})
|
||||
|
||||
test-start-docker-nginx-w-subpath:
|
||||
@(docker run \
|
||||
|
||||
35
README.md
35
README.md
@@ -25,21 +25,7 @@ A graphical user interface for [MinIO](https://github.com/minio/minio)
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
|
||||
## Install
|
||||
|
||||
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.
|
||||
|
||||
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.21
|
||||
|
||||
```
|
||||
go install github.com/minio/console/cmd/console@latest
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -205,6 +191,27 @@ export CONSOLE_MINIO_SERVER=https://localhost:9000
|
||||
|
||||
You can verify that the apis work by doing the request on `localhost:9090/api/v1/...`
|
||||
|
||||
## Debug logging
|
||||
|
||||
In some cases it may be convenient to log all HTTP requests. This can be enabled by setting
|
||||
the `CONSOLE_DEBUG_LOGLEVEL` environment variable to one of the following values:
|
||||
|
||||
- `0` (default) uses no logging.
|
||||
- `1` log single line per request for server-side errors (status-code 5xx).
|
||||
- `2` log single line per request for client-side and server-side errors (status-code 4xx/5xx).
|
||||
- `3` log single line per request for all requests (status-code 4xx/5xx).
|
||||
- `4` log details per request for server-side errors (status-code 5xx).
|
||||
- `5` log details per request for client-side and server-side errors (status-code 4xx/5xx).
|
||||
- `6` log details per request for all requests (status-code 4xx/5xx).
|
||||
|
||||
A single line logging has the following information:
|
||||
- Remote endpoint (IP + port) of the request. Note that reverse proxies may hide the actual remote endpoint of the client's browser.
|
||||
- HTTP method and URL
|
||||
- Status code of the response (websocket connections are hijacked, so no response is shown)
|
||||
- Duration of the request
|
||||
|
||||
The detailed logging also includes all request and response headers (if any).
|
||||
|
||||
# Contribute to console Project
|
||||
|
||||
Please follow console [Contributor's Guide](https://github.com/minio/console/blob/master/CONTRIBUTING.md)
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func registerAdminArnsHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of arns
|
||||
api.SystemArnListHandler = systemApi.ArnListHandlerFunc(func(params systemApi.ArnListParams, session *models.Principal) middleware.Responder {
|
||||
arnsResp, err := getArnsResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewArnListDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return systemApi.NewArnListOK().WithPayload(arnsResp)
|
||||
})
|
||||
}
|
||||
|
||||
// getArns invokes admin info and returns a list of arns
|
||||
func getArns(ctx context.Context, client MinioAdmin) (*models.ArnsResponse, error) {
|
||||
serverInfo, err := client.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// build response
|
||||
return &models.ArnsResponse{
|
||||
Arns: serverInfo.SQSARN,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getArnsResponse returns a list of active arns in the instance
|
||||
func getArnsResponse(session *models.Principal, params systemApi.ArnListParams) (*models.ArnsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
// serialize output
|
||||
arnsList, err := getArns(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return arnsList, nil
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations/system"
|
||||
"github.com/minio/console/models"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestArnsList(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
// Test-1 : getArns() returns proper arn list
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{
|
||||
SQSARN: []string{"uno"},
|
||||
}, nil
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
arnsList, err := getArns(ctx, adminClient)
|
||||
assert.NotNil(arnsList, "arn list was returned nil")
|
||||
if arnsList != nil {
|
||||
assert.Equal(len(arnsList.Arns), 1, "Incorrect arns count")
|
||||
}
|
||||
assert.Nil(err, "Error should have been nil")
|
||||
|
||||
// Test-2 : getArns(ctx) fails for whatever reason
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, errors.New("some reason")
|
||||
}
|
||||
|
||||
arnsList, err = getArns(ctx, adminClient)
|
||||
assert.Nil(arnsList, "arn list was not returned nil")
|
||||
assert.NotNil(err, "An error should have been returned")
|
||||
}
|
||||
|
||||
func TestRegisterAdminArnsHandlers(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
swaggerSpec, err := loads.Embedded(SwaggerJSON, FlatSwaggerJSON)
|
||||
if err != nil {
|
||||
assert.Fail("Error")
|
||||
}
|
||||
api := operations.NewConsoleAPI(swaggerSpec)
|
||||
api.SystemArnListHandler = nil
|
||||
registerAdminArnsHandlers(api)
|
||||
if api.SystemArnListHandler == nil {
|
||||
assert.Fail("Assignment should happen")
|
||||
} else {
|
||||
fmt.Println("Function got assigned: ", api.SystemArnListHandler)
|
||||
}
|
||||
|
||||
// To test error case in registerAdminArnsHandlers
|
||||
request, _ := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/buckets/",
|
||||
nil,
|
||||
)
|
||||
ArnListParamsStruct := system.ArnListParams{
|
||||
HTTPRequest: request,
|
||||
}
|
||||
modelsPrincipal := models.Principal{
|
||||
STSAccessKeyID: "accesskey",
|
||||
}
|
||||
var value middleware.Responder = api.SystemArnListHandler.Handle(ArnListParamsStruct, &modelsPrincipal)
|
||||
str := fmt.Sprintf("%#v", value)
|
||||
fmt.Println("value: ", str)
|
||||
assert.Equal(strings.Contains(str, "_statusCode:500"), true)
|
||||
}
|
||||
@@ -18,385 +18,18 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
)
|
||||
|
||||
type AdminClientMock struct{}
|
||||
|
||||
var (
|
||||
MinioServerInfoMock func(ctx context.Context) (madmin.InfoMessage, error)
|
||||
minioChangePasswordMock func(ctx context.Context, accessKey, secretKey string) error
|
||||
|
||||
minioHelpConfigKVMock func(subSys, key string, envOnly bool) (madmin.Help, error)
|
||||
minioGetConfigKVMock func(key string) ([]byte, error)
|
||||
minioSetConfigKVMock func(kv string) (restart bool, err error)
|
||||
minioDelConfigKVMock func(name string) (err error)
|
||||
minioHelpConfigKVGlobalMock func(envOnly bool) (madmin.Help, error)
|
||||
|
||||
minioGetLogsMock func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
|
||||
minioListGroupsMock func() ([]string, error)
|
||||
minioUpdateGroupMembersMock func(madmin.GroupAddRemove) error
|
||||
minioGetGroupDescriptionMock func(group string) (*madmin.GroupDesc, error)
|
||||
minioSetGroupStatusMock func(group string, status madmin.GroupStatus) error
|
||||
|
||||
minioHealMock func(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
|
||||
|
||||
minioServerHealthInfoMock func(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error)
|
||||
|
||||
minioListPoliciesMock func() (map[string]*iampolicy.Policy, error)
|
||||
minioGetPolicyMock func(name string) (*iampolicy.Policy, error)
|
||||
minioRemovePolicyMock func(name string) error
|
||||
minioAddPolicyMock func(name string, policy *iampolicy.Policy) error
|
||||
minioSetPolicyMock func(policyName, entityName string, isGroup bool) error
|
||||
|
||||
minioStartProfiling func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error)
|
||||
minioStopProfiling func() (io.ReadCloser, error)
|
||||
|
||||
minioServiceRestartMock func(ctx context.Context) error
|
||||
|
||||
getSiteReplicationInfo func(ctx context.Context) (*madmin.SiteReplicationInfo, error)
|
||||
addSiteReplicationInfo func(ctx context.Context, sites []madmin.PeerSite) (*madmin.ReplicateAddStatus, error)
|
||||
editSiteReplicationInfo func(ctx context.Context, site madmin.PeerInfo) (*madmin.ReplicateEditStatus, error)
|
||||
deleteSiteReplicationInfoMock func(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
|
||||
getSiteReplicationStatus func(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
|
||||
|
||||
minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error)
|
||||
minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error)
|
||||
minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error
|
||||
minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error
|
||||
|
||||
minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
|
||||
minioListUsersMock func() (map[string]madmin.UserInfo, error)
|
||||
minioAddUserMock func(accessKey, secreyKey string) error
|
||||
minioRemoveUserMock func(accessKey string) error
|
||||
minioGetUserInfoMock func(accessKey string) (madmin.UserInfo, error)
|
||||
minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
|
||||
|
||||
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
|
||||
minioAddServiceAccountMock func(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error)
|
||||
minioListServiceAccountsMock func(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
|
||||
minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
|
||||
minioInfoServiceAccountMock func(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
|
||||
minioUpdateServiceAccountMock func(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
|
||||
minioGetLDAPPolicyEntitiesMock func(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
|
||||
|
||||
minioListRemoteBucketsMock func(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
|
||||
minioGetRemoteBucketMock func(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
|
||||
minioAddRemoteBucketMock func(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
|
||||
)
|
||||
|
||||
func (ac AdminClientMock) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
return MinioServerInfoMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
|
||||
return minioListRemoteBucketsMock(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error) {
|
||||
return minioGetRemoteBucketMock(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removeRemoteBucket(_ context.Context, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
|
||||
return minioAddRemoteBucketMock(ctx, bucket, target)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) changePassword(ctx context.Context, accessKey, secretKey string) error {
|
||||
return minioChangePasswordMock(ctx, accessKey, secretKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) speedtest(_ context.Context, _ madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) verifyTierStatus(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// mock function helpConfigKV()
|
||||
func (ac AdminClientMock) helpConfigKV(_ context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return minioHelpConfigKVMock(subSys, key, envOnly)
|
||||
}
|
||||
|
||||
// mock function getConfigKV()
|
||||
func (ac AdminClientMock) getConfigKV(_ context.Context, name string) ([]byte, error) {
|
||||
return minioGetConfigKVMock(name)
|
||||
}
|
||||
|
||||
// mock function setConfigKV()
|
||||
func (ac AdminClientMock) setConfigKV(_ context.Context, kv string) (restart bool, err error) {
|
||||
return minioSetConfigKVMock(kv)
|
||||
}
|
||||
|
||||
// mock function helpConfigKV()
|
||||
func (ac AdminClientMock) helpConfigKVGlobal(_ context.Context, envOnly bool) (madmin.Help, error) {
|
||||
return minioHelpConfigKVGlobalMock(envOnly)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) delConfigKV(_ context.Context, name string) (err error) {
|
||||
return minioDelConfigKVMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return minioGetLogsMock(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listGroups(_ context.Context) ([]string, error) {
|
||||
return minioListGroupsMock()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) updateGroupMembers(_ context.Context, req madmin.GroupAddRemove) error {
|
||||
return minioUpdateGroupMembersMock(req)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getGroupDescription(_ context.Context, group string) (*madmin.GroupDesc, error) {
|
||||
return minioGetGroupDescriptionMock(group)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setGroupStatus(_ context.Context, group string, status madmin.GroupStatus) error {
|
||||
return minioSetGroupStatusMock(group, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool,
|
||||
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
|
||||
return minioHealMock(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) serverHealthInfo(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error) {
|
||||
return minioServerHealthInfoMock(ctx, healthDataTypes, deadline)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addOrUpdateIDPConfig(_ context.Context, _, _, _ string, _ bool) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listIDPConfig(_ context.Context, _ string) ([]madmin.IDPListItem, error) {
|
||||
return []madmin.IDPListItem{{Name: "mock"}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteIDPConfig(_ context.Context, _, _ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getIDPConfig(_ context.Context, _, _ string) (c madmin.IDPConfig, err error) {
|
||||
return madmin.IDPConfig{Info: []madmin.IDPCfgInfo{{Key: "mock", Value: "mock"}}}, nil
|
||||
type AdminClientMock struct {
|
||||
minioAccountInfoMock func(ctx context.Context) (madmin.AccountInfo, error)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsStatus(_ context.Context) (madmin.KMSStatus, error) {
|
||||
return madmin.KMSStatus{Name: "name", DefaultKeyID: "key", Endpoints: map[string]madmin.ItemState{"localhost": madmin.ItemState("online")}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsAPIs(_ context.Context) ([]madmin.KMSAPI, error) {
|
||||
return []madmin.KMSAPI{{Method: "GET", Path: "/mock"}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsMetrics(_ context.Context) (*madmin.KMSMetrics, error) {
|
||||
return &madmin.KMSMetrics{}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) kmsVersion(_ context.Context) (*madmin.KMSVersion, error) {
|
||||
return &madmin.KMSVersion{Version: "test-version"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) createKey(_ context.Context, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) importKey(_ context.Context, _ string, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listKeys(_ context.Context, _ string) ([]madmin.KMSKeyInfo, error) {
|
||||
return []madmin.KMSKeyInfo{{
|
||||
Name: "name",
|
||||
CreatedBy: "by",
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) keyStatus(_ context.Context, _ string) (*madmin.KMSKeyStatus, error) {
|
||||
return &madmin.KMSKeyStatus{KeyID: "key"}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) 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()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getPolicy(_ context.Context, name string) (*iampolicy.Policy, error) {
|
||||
return minioGetPolicyMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removePolicy(_ context.Context, name string) error {
|
||||
return minioRemovePolicyMock(name)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addPolicy(_ context.Context, name string, policy *iampolicy.Policy) error {
|
||||
return minioAddPolicyMock(name, policy)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setPolicy(_ context.Context, policyName, entityName string, isGroup bool) error {
|
||||
return minioSetPolicyMock(policyName, entityName, isGroup)
|
||||
}
|
||||
|
||||
// mock function for startProfiling()
|
||||
func (ac AdminClientMock) startProfiling(_ context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return minioStartProfiling(profiler)
|
||||
}
|
||||
|
||||
// mock function for stopProfiling()
|
||||
func (ac AdminClientMock) stopProfiling(_ context.Context) (io.ReadCloser, error) {
|
||||
return minioStopProfiling()
|
||||
}
|
||||
|
||||
// mock function of serviceRestart()
|
||||
func (ac AdminClientMock) serviceRestart(ctx context.Context) error {
|
||||
return minioServiceRestartMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
|
||||
return getSiteReplicationInfo(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, _ madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
|
||||
return addSiteReplicationInfo(ctx, sites)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, _ madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
|
||||
return editSiteReplicationInfo(ctx, site)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
|
||||
return deleteSiteReplicationInfoMock(ctx, removeReq)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
|
||||
return getSiteReplicationStatus(ctx, params)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
|
||||
return minioListTiersMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
|
||||
return minioTierStatsMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig) error {
|
||||
return minioAddTiersMock(ctx, tier)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
|
||||
return minioEditTiersMock(ctx, tierName, creds)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
return minioServiceTraceMock(ctx, threshold, s3, internal, storage, os, errTrace)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listUsers(_ context.Context) (map[string]madmin.UserInfo, error) {
|
||||
return minioListUsersMock()
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addUser(_ context.Context, accessKey, secretKey string) error {
|
||||
return minioAddUserMock(accessKey, secretKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) removeUser(_ context.Context, accessKey string) error {
|
||||
return minioRemoveUserMock(accessKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getUserInfo(_ context.Context, accessKey string) (madmin.UserInfo, error) {
|
||||
return minioGetUserInfoMock(accessKey)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) setUserStatus(_ context.Context, accessKey string, status madmin.AccountStatus) error {
|
||||
return minioSetUserStatusMock(accessKey, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
|
||||
return minioAccountInfoMock(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, description string, name string, expiry *time.Time, status string) (madmin.Credentials, error) {
|
||||
return minioAddServiceAccountMock(ctx, policy, user, accessKey, secretKey, description, name, expiry, status)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
|
||||
return minioListServiceAccountsMock(ctx, user)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return minioDeleteServiceAccountMock(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
|
||||
return minioInfoServiceAccountMock(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
|
||||
return minioUpdateServiceAccountMock(ctx, serviceAccount, opts)
|
||||
}
|
||||
|
||||
func (ac AdminClientMock) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return minioGetLDAPPolicyEntitiesMock(ctx, query)
|
||||
return ac.minioAccountInfoMock(ctx)
|
||||
}
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
madmin "github.com/minio/madmin-go/v3"
|
||||
|
||||
cfgApi "github.com/minio/console/api/operations/configuration"
|
||||
)
|
||||
|
||||
func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
// List Configurations
|
||||
api.ConfigurationListConfigHandler = cfgApi.ListConfigHandlerFunc(func(params cfgApi.ListConfigParams, session *models.Principal) middleware.Responder {
|
||||
configListResp, err := getListConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewListConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewListConfigOK().WithPayload(configListResp)
|
||||
})
|
||||
// Configuration Info
|
||||
api.ConfigurationConfigInfoHandler = cfgApi.ConfigInfoHandlerFunc(func(params cfgApi.ConfigInfoParams, session *models.Principal) middleware.Responder {
|
||||
config, err := getConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewConfigInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewConfigInfoOK().WithPayload(config)
|
||||
})
|
||||
// Set Configuration
|
||||
api.ConfigurationSetConfigHandler = cfgApi.SetConfigHandlerFunc(func(params cfgApi.SetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := setConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewSetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewSetConfigOK().WithPayload(resp)
|
||||
})
|
||||
// Reset Configuration
|
||||
api.ConfigurationResetConfigHandler = cfgApi.ResetConfigHandlerFunc(func(params cfgApi.ResetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := resetConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewResetConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewResetConfigOK().WithPayload(resp)
|
||||
})
|
||||
// Export Configuration as base64 string.
|
||||
api.ConfigurationExportConfigHandler = cfgApi.ExportConfigHandlerFunc(func(params cfgApi.ExportConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := exportConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewExportConfigDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewExportConfigOK().WithPayload(resp)
|
||||
})
|
||||
api.ConfigurationPostConfigsImportHandler = cfgApi.PostConfigsImportHandlerFunc(func(params cfgApi.PostConfigsImportParams, session *models.Principal) middleware.Responder {
|
||||
_, err := importConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewPostConfigsImportDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return cfgApi.NewPostConfigsImportDefault(200)
|
||||
})
|
||||
}
|
||||
|
||||
// listConfig gets all configurations' names and their descriptions
|
||||
func listConfig(client MinioAdmin) ([]*models.ConfigDescription, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
configKeysHelp, err := client.helpConfigKV(ctx, "", "", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configDescs []*models.ConfigDescription
|
||||
for _, c := range configKeysHelp.KeysHelp {
|
||||
desc := &models.ConfigDescription{
|
||||
Key: c.Key,
|
||||
Description: c.Description,
|
||||
}
|
||||
configDescs = append(configDescs, desc)
|
||||
}
|
||||
return configDescs, nil
|
||||
}
|
||||
|
||||
// getListConfigResponse performs listConfig() and serializes it to the handler's output
|
||||
func getListConfigResponse(session *models.Principal, params cfgApi.ListConfigParams) (*models.ListConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
configDescs, err := listConfig(adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
listGroupsResponse := &models.ListConfigResponse{
|
||||
Configurations: configDescs,
|
||||
Total: int64(len(configDescs)),
|
||||
}
|
||||
return listGroupsResponse, nil
|
||||
}
|
||||
|
||||
// getConfig gets the key values for a defined configuration.
|
||||
func getConfig(ctx context.Context, client MinioAdmin, name string) ([]*models.Configuration, error) {
|
||||
configBytes, err := client.getConfigKV(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subSysConfigs, err := madmin.ParseServerConfigOutput(string(configBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configSubSysList []*models.Configuration
|
||||
for _, scfg := range subSysConfigs {
|
||||
if !madmin.SubSystems.Contains(scfg.SubSystem) {
|
||||
return nil, fmt.Errorf("no sub-systems found")
|
||||
}
|
||||
var confkv []*models.ConfigurationKV
|
||||
for _, kv := range scfg.KV {
|
||||
var envOverride *models.EnvOverride
|
||||
|
||||
if kv.EnvOverride != nil {
|
||||
envOverride = &models.EnvOverride{
|
||||
Name: kv.EnvOverride.Name,
|
||||
Value: kv.EnvOverride.Value,
|
||||
}
|
||||
}
|
||||
|
||||
confkv = append(confkv, &models.ConfigurationKV{Key: kv.Key, Value: kv.Value, EnvOverride: envOverride})
|
||||
}
|
||||
if len(confkv) == 0 {
|
||||
continue
|
||||
}
|
||||
var fullConfigName string
|
||||
if scfg.Target == "" {
|
||||
fullConfigName = scfg.SubSystem
|
||||
} else {
|
||||
fullConfigName = scfg.SubSystem + ":" + scfg.Target
|
||||
}
|
||||
configSubSysList = append(configSubSysList, &models.Configuration{KeyValues: confkv, Name: fullConfigName})
|
||||
}
|
||||
return configSubSysList, nil
|
||||
}
|
||||
|
||||
// getConfigResponse performs getConfig() and serializes it to the handler's output
|
||||
func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams) ([]*models.Configuration, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
configurations, err := getConfig(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
errorVal := ErrorWithContext(ctx, err)
|
||||
minioError := madmin.ToErrorResponse(err)
|
||||
if minioError.Code == "XMinioConfigError" {
|
||||
errorVal.Code = 404
|
||||
}
|
||||
return nil, errorVal
|
||||
}
|
||||
return configurations, nil
|
||||
}
|
||||
|
||||
// setConfig sets a configuration with the defined key values
|
||||
func setConfig(ctx context.Context, client MinioAdmin, configName *string, kvs []*models.ConfigurationKV) (restart bool, err error) {
|
||||
config := buildConfig(configName, kvs)
|
||||
restart, err = client.setConfigKV(ctx, *config)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func setConfigWithARNAccountID(ctx context.Context, client MinioAdmin, configName *string, kvs []*models.ConfigurationKV, arnAccountID string) (restart bool, err error) {
|
||||
// if arnAccountID is not empty the configuration will be treated as a notification target
|
||||
// arnAccountID will be used as an identifier for that specific target
|
||||
// docs: https://min.io/docs/minio/linux/administration/monitoring/bucket-notifications.html
|
||||
if arnAccountID != "" {
|
||||
configName = swag.String(fmt.Sprintf("%s:%s", *configName, arnAccountID))
|
||||
}
|
||||
return setConfig(ctx, client, configName, kvs)
|
||||
}
|
||||
|
||||
// buildConfig builds a concatenated string including name and keyvalues
|
||||
// e.g. `region name=us-west-1`
|
||||
func buildConfig(configName *string, kvs []*models.ConfigurationKV) *string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString(*configName)
|
||||
for _, kv := range kvs {
|
||||
key := strings.TrimSpace(kv.Key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString(key)
|
||||
builder.WriteString("=")
|
||||
// All newlines must be converted to ','
|
||||
builder.WriteString(strings.ReplaceAll(strings.TrimSpace(fmt.Sprintf("\"%s\"", kv.Value)), "\n", ","))
|
||||
}
|
||||
config := builder.String()
|
||||
return &config
|
||||
}
|
||||
|
||||
// setConfigResponse implements setConfig() to be used by handler
|
||||
func setConfigResponse(session *models.Principal, params cfgApi.SetConfigParams) (*models.SetConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
configName := params.Name
|
||||
|
||||
needsRestart, err := setConfigWithARNAccountID(ctx, adminClient, &configName, params.Body.KeyValues, params.Body.ArnResourceID)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetConfigResponse{Restart: needsRestart}, nil
|
||||
}
|
||||
|
||||
func resetConfig(ctx context.Context, client MinioAdmin, configName *string) (err error) {
|
||||
err = client.delConfigKV(ctx, *configName)
|
||||
return err
|
||||
}
|
||||
|
||||
// resetConfigResponse implements resetConfig() to be used by handler
|
||||
func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigParams) (*models.SetConfigResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
err = resetConfig(ctx, adminClient, ¶ms.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return &models.SetConfigResponse{Restart: true}, nil
|
||||
}
|
||||
|
||||
func exportConfigResponse(session *models.Principal, params cfgApi.ExportConfigParams) (*models.ConfigExportResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
configRes, err := mAdmin.GetConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// may contain sensitive information so unpack only when required.
|
||||
return &models.ConfigExportResponse{
|
||||
Status: "success",
|
||||
Value: base64.StdEncoding.EncodeToString(configRes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func importConfigResponse(session *models.Principal, params cfgApi.PostConfigsImportParams) (*cfgApi.PostConfigsImportDefault, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
file, _, err := params.HTTPRequest.FormFile("file")
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = mAdmin.SetConfig(ctx, file)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &cfgApi.PostConfigsImportDefault{}, nil
|
||||
}
|
||||
@@ -1,609 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
NotifyPostgresSubSys = "notify_postgres"
|
||||
PostgresFormat = "format"
|
||||
PostgresConnectionString = "connection_string"
|
||||
PostgresTable = "table"
|
||||
PostgresQueueDir = "queue_dir"
|
||||
PostgresQueueLimit = "queue_limit"
|
||||
)
|
||||
|
||||
func TestListConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
function := "listConfig()"
|
||||
// Test-1 : listConfig() get list of two configurations and ensure is output correctly
|
||||
configListMock := []madmin.HelpKV{
|
||||
{
|
||||
Key: "region",
|
||||
Description: "label the location of the server",
|
||||
},
|
||||
{
|
||||
Key: "notify_nsq",
|
||||
Description: "publish bucket notifications to NSQ endpoints",
|
||||
},
|
||||
}
|
||||
mockConfigList := madmin.Help{
|
||||
SubSys: "sys",
|
||||
Description: "desc",
|
||||
MultipleTargets: false,
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
expectedKeysDesc := mockConfigList.KeysHelp
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
configList, err := listConfig(adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// verify length of keys is correct
|
||||
assert.Equal(len(expectedKeysDesc), len(configList), fmt.Sprintf("Failed on %s: length of Configs's lists is not the same", function))
|
||||
// verify KeysHelp content
|
||||
for i, kv := range configList {
|
||||
assert.Equal(expectedKeysDesc[i].Key, kv.Key)
|
||||
assert.Equal(expectedKeysDesc[i].Description, kv.Description)
|
||||
}
|
||||
|
||||
// Test-2 : listConfig() Return error and see that the error is handled correctly and returned
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return madmin.Help{}, errors.New("error")
|
||||
}
|
||||
_, err = listConfig(adminClient)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setConfig()"
|
||||
// mock function response from setConfig()
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
configName := "notify_postgres"
|
||||
kvs := []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "enable",
|
||||
Value: "off",
|
||||
},
|
||||
{
|
||||
Key: "connection_string",
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : setConfig() sets a config with two key value pairs
|
||||
restart, err := setConfig(ctx, adminClient, &configName, kvs)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(restart, false)
|
||||
|
||||
// Test-2 : setConfig() returns error, handle properly
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
}
|
||||
restart, err = setConfig(ctx, adminClient, &configName, kvs)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
assert.Equal(restart, false)
|
||||
|
||||
// Test-4 : setConfig() set config, need restart
|
||||
minioSetConfigKVMock = func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
restart, err = setConfig(ctx, adminClient, &configName, kvs)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(restart, true)
|
||||
}
|
||||
|
||||
func TestDelConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
function := "resetConfig()"
|
||||
// mock function response from setConfig()
|
||||
minioDelConfigKVMock = func(_ string) (err error) {
|
||||
return nil
|
||||
}
|
||||
configName := "region"
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : resetConfig() resets a config with the config name
|
||||
err := resetConfig(ctx, adminClient, &configName)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : resetConfig() returns error, handle properly
|
||||
minioDelConfigKVMock = func(_ string) (err error) {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
err = resetConfig(ctx, adminClient, &configName)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildConfig(t *testing.T) {
|
||||
type args struct {
|
||||
configName *string
|
||||
kvs []*models.ConfigurationKV
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *string
|
||||
}{
|
||||
// Test-1: buildConfig() format correctly configuration as "config_name k=v k2=v2"
|
||||
{
|
||||
name: "format correctly",
|
||||
args: args{
|
||||
configName: swag.String("notify_postgres"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "enable",
|
||||
Value: "off",
|
||||
},
|
||||
{
|
||||
Key: "connection_string",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: swag.String("notify_postgres enable=\"off\" connection_string=\"\""),
|
||||
},
|
||||
// Test-2: buildConfig() format correctly configuration as "config_name k=v k2=v2 k2=v3" with duplicate keys
|
||||
{
|
||||
name: "duplicated keys in config",
|
||||
args: args{
|
||||
configName: swag.String("notify_postgres"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "enable",
|
||||
Value: "off",
|
||||
},
|
||||
{
|
||||
Key: "connection_string",
|
||||
Value: "",
|
||||
},
|
||||
{
|
||||
Key: "connection_string",
|
||||
Value: "x",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: swag.String("notify_postgres enable=\"off\" connection_string=\"\" connection_string=\"x\""),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_setConfigWithARN(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
client := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
client MinioAdmin
|
||||
configName *string
|
||||
kvs []*models.ConfigurationKV
|
||||
arn string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
mockSetConfig func(kv string) (restart bool, err error)
|
||||
wantErr bool
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "Set valid config with arn",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
configName: swag.String("notify_kafka"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "brokers",
|
||||
Value: "http://localhost:8080/broker1,http://localhost:8080/broker2",
|
||||
},
|
||||
},
|
||||
arn: "1",
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
wantErr: false,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Set valid config, expect restart",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
configName: swag.String("notify_kafka"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "brokers",
|
||||
Value: "http://localhost:8080/broker1,http://localhost:8080/broker2",
|
||||
},
|
||||
},
|
||||
arn: "1",
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
},
|
||||
wantErr: false,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Set valid config without arn",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
configName: swag.String("region"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "name",
|
||||
Value: "us-west-1",
|
||||
},
|
||||
},
|
||||
arn: "",
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
wantErr: false,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Setting an incorrect config",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
configName: swag.String("oorgle"),
|
||||
kvs: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: "name",
|
||||
Value: "us-west-1",
|
||||
},
|
||||
},
|
||||
arn: "",
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
},
|
||||
wantErr: true,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("setConfigWithARNAccountID() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
assert.Equal(restart, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getConfig(t *testing.T) {
|
||||
client := AdminClientMock{}
|
||||
type args struct {
|
||||
client MinioAdmin
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
mock func()
|
||||
want []*models.Configuration
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "get config",
|
||||
args: args{
|
||||
client: client,
|
||||
name: "notify_postgres",
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
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
|
||||
}
|
||||
|
||||
configListMock := []madmin.HelpKV{
|
||||
{
|
||||
Key: PostgresConnectionString,
|
||||
Description: `Postgres server connection-string e.g. "host=localhost port=5432 dbname=minio_events user=postgres password=password sslmode=disable"`,
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresTable,
|
||||
Description: "DB table name to store/update events, table is auto-created",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresFormat,
|
||||
Description: "desc",
|
||||
Type: "namespace*|access",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueDir,
|
||||
Description: "des",
|
||||
Optional: true,
|
||||
Type: "path",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueLimit,
|
||||
Description: "desc",
|
||||
Optional: true,
|
||||
Type: "number",
|
||||
},
|
||||
{
|
||||
Key: madmin.CommentKey,
|
||||
Description: "",
|
||||
Optional: true,
|
||||
Type: "sentence",
|
||||
},
|
||||
}
|
||||
mockConfigList := madmin.Help{
|
||||
SubSys: NotifyPostgresSubSys,
|
||||
Description: "publish bucket notifications to Postgres databases",
|
||||
MultipleTargets: true,
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
want: []*models.Configuration{
|
||||
{
|
||||
KeyValues: []*models.ConfigurationKV{
|
||||
{
|
||||
Key: PostgresConnectionString,
|
||||
Value: "host=localhost dbname=minio_events user=postgres password=password port=5432 sslmode=disable",
|
||||
},
|
||||
{
|
||||
Key: PostgresTable,
|
||||
Value: "bucketevents",
|
||||
},
|
||||
}, Name: "notify_postgres",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid config, but server returned empty",
|
||||
args: args{
|
||||
client: client,
|
||||
name: NotifyPostgresSubSys,
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return []byte(`notify_postgres:_`), nil
|
||||
}
|
||||
|
||||
configListMock := []madmin.HelpKV{
|
||||
{
|
||||
Key: PostgresConnectionString,
|
||||
Description: `Postgres server connection-string e.g. "host=localhost port=5432 dbname=minio_events user=postgres password=password sslmode=disable"`,
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresTable,
|
||||
Description: "DB table name to store/update events, table is auto-created",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresFormat,
|
||||
Description: "desc",
|
||||
Type: "namespace*|access",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueDir,
|
||||
Description: "des",
|
||||
Optional: true,
|
||||
Type: "path",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueLimit,
|
||||
Description: "desc",
|
||||
Optional: true,
|
||||
Type: "number",
|
||||
},
|
||||
{
|
||||
Key: madmin.CommentKey,
|
||||
Description: "optionally add a comment to this setting",
|
||||
Optional: true,
|
||||
Type: "sentence",
|
||||
},
|
||||
}
|
||||
mockConfigList := madmin.Help{
|
||||
SubSys: NotifyPostgresSubSys,
|
||||
Description: "publish bucket notifications to Postgres databases",
|
||||
MultipleTargets: true,
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
want: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "random bytes coming out of getConfigKv",
|
||||
args: args{
|
||||
client: client,
|
||||
name: "notify_postgres",
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
x := make(map[string]string)
|
||||
x["x"] = "x"
|
||||
j, _ := json.Marshal(x)
|
||||
return j, nil
|
||||
}
|
||||
|
||||
configListMock := []madmin.HelpKV{
|
||||
{
|
||||
Key: PostgresConnectionString,
|
||||
Description: `Postgres server connection-string e.g. "host=localhost port=5432 dbname=minio_events user=postgres password=password sslmode=disable"`,
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresTable,
|
||||
Description: "DB table name to store/update events, table is auto-created",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Key: PostgresFormat,
|
||||
Description: "desc",
|
||||
Type: "namespace*|access",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueDir,
|
||||
Description: "des",
|
||||
Optional: true,
|
||||
Type: "path",
|
||||
},
|
||||
{
|
||||
Key: PostgresQueueLimit,
|
||||
Description: "desc",
|
||||
Optional: true,
|
||||
Type: "number",
|
||||
},
|
||||
{
|
||||
Key: madmin.CommentKey,
|
||||
Description: "optionally add a comment to this setting",
|
||||
Optional: true,
|
||||
Type: "sentence",
|
||||
},
|
||||
}
|
||||
mockConfigList := madmin.Help{
|
||||
SubSys: NotifyPostgresSubSys,
|
||||
Description: "publish bucket notifications to Postgres databases",
|
||||
MultipleTargets: true,
|
||||
KeysHelp: configListMock,
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "bad config",
|
||||
args: args{
|
||||
client: client,
|
||||
name: "notify_postgresx",
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return nil, errors.New("invalid config")
|
||||
}
|
||||
|
||||
mockConfigList := madmin.Help{}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return mockConfigList, nil
|
||||
}
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no help",
|
||||
args: args{
|
||||
client: client,
|
||||
name: "notify_postgresx",
|
||||
},
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return nil, errors.New("invalid config")
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(_, _ string, _ bool) (madmin.Help, error) {
|
||||
return madmin.Help{}, errors.New("no help")
|
||||
}
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt.mock()
|
||||
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)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("getConfig() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
const logTimeFormat string = "15:04:05 MST 01/02/2006"
|
||||
|
||||
// startConsoleLog starts log of the servers
|
||||
func startConsoleLog(ctx context.Context, conn WSConn, client MinioAdmin, logRequest LogRequest) error {
|
||||
var node string
|
||||
// name of node, default = "" (all)
|
||||
if logRequest.node == "all" {
|
||||
node = ""
|
||||
} else {
|
||||
node = logRequest.node
|
||||
}
|
||||
|
||||
trimNode := strings.Split(node, ":")
|
||||
// number of log lines
|
||||
lineCount := 100
|
||||
// type of logs "minio"|"application"|"all" default = "all"
|
||||
var logKind string
|
||||
if logRequest.logType == "minio" || logRequest.logType == "application" || logRequest.logType == "all" {
|
||||
logKind = logRequest.logType
|
||||
} else {
|
||||
logKind = "all"
|
||||
}
|
||||
|
||||
// Start listening on all Console Log activity.
|
||||
logCh := client.getLogs(ctx, trimNode[0], lineCount, logKind)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case logInfo, ok := <-logCh:
|
||||
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if logInfo.Err != nil {
|
||||
LogError("error on console logs: %v", logInfo.Err)
|
||||
return logInfo.Err
|
||||
}
|
||||
|
||||
// Serialize message to be sent
|
||||
bytes, err := json.Marshal(serializeConsoleLogInfo(&logInfo))
|
||||
if err != nil {
|
||||
LogError("error on json.Marshal: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
LogError("error writeMessage: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serializeConsoleLogInfo(l *madmin.LogInfo) (logInfo madmin.LogInfo) {
|
||||
logInfo = *l
|
||||
if logInfo.ConsoleMsg != "" {
|
||||
logInfo.ConsoleMsg = strings.TrimPrefix(logInfo.ConsoleMsg, "\n")
|
||||
}
|
||||
if logInfo.Time != "" {
|
||||
logInfo.Time = getLogTime(logInfo.Time)
|
||||
}
|
||||
return logInfo
|
||||
}
|
||||
|
||||
func getLogTime(lt string) string {
|
||||
tm, err := time.Parse(time.RFC3339Nano, lt)
|
||||
if err != nil {
|
||||
return lt
|
||||
}
|
||||
return tm.Format(logTimeFormat)
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAdminConsoleLog(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startConsoleLog(ctx, )"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
testReceiver := make(chan madmin.LogInfo, 5)
|
||||
textToReceive := "test message"
|
||||
testStreamSize := 5
|
||||
isClosed := false // testReceiver is closed?
|
||||
|
||||
// Test-1: Serve Console with no errors until Console finishes sending
|
||||
// define mock function behavior for minio server Console
|
||||
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) {
|
||||
defer close(ch)
|
||||
lines := make([]int, testStreamSize)
|
||||
// mocking sending 5 lines of info
|
||||
for range lines {
|
||||
info := madmin.LogInfo{
|
||||
ConsoleMsg: textToReceive,
|
||||
}
|
||||
ch <- info
|
||||
}
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
writesCount := 1
|
||||
// mock connection WriteMessage() no error
|
||||
connWriteMessageMock = func(_ int, data []byte) error {
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.LogInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
if writesCount == testStreamSize {
|
||||
if !isClosed {
|
||||
close(testReceiver)
|
||||
isClosed = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
testReceiver <- t
|
||||
writesCount++
|
||||
return nil
|
||||
}
|
||||
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// check that the TestReceiver got the same number of data from Console.
|
||||
for i := range testReceiver {
|
||||
assert.Equal(textToReceive, i.ConsoleMsg)
|
||||
}
|
||||
|
||||
// Test-2: if error happens while writing, return 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) {
|
||||
assert.Equal("error on write", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: error happens on GetLogs Minio, Console should stop
|
||||
// and error shall be returned.
|
||||
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) {
|
||||
defer close(ch)
|
||||
lines := make([]int, 2)
|
||||
// mocking sending 5 lines of info
|
||||
for range lines {
|
||||
info := madmin.LogInfo{
|
||||
ConsoleMsg: textToReceive,
|
||||
}
|
||||
ch <- info
|
||||
}
|
||||
ch <- madmin.LogInfo{Err: fmt.Errorf("error on Console")}
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
if err := startConsoleLog(ctx, mockWSConn, adminClient, LogRequest{node: "", logType: "all"}); assert.Error(err) {
|
||||
assert.Equal("error on Console", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
groupApi "github.com/minio/console/api/operations/group"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
// List Groups
|
||||
api.GroupListGroupsHandler = groupApi.ListGroupsHandlerFunc(func(params groupApi.ListGroupsParams, session *models.Principal) middleware.Responder {
|
||||
listGroupsResponse, err := getListGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return groupApi.NewListGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewListGroupsOK().WithPayload(listGroupsResponse)
|
||||
})
|
||||
// Group Info
|
||||
api.GroupGroupInfoHandler = groupApi.GroupInfoHandlerFunc(func(params groupApi.GroupInfoParams, session *models.Principal) middleware.Responder {
|
||||
groupInfo, err := getGroupInfoResponse(session, params)
|
||||
if err != nil {
|
||||
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(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(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return groupApi.NewRemoveGroupNoContent()
|
||||
})
|
||||
// Update Group
|
||||
api.GroupUpdateGroupHandler = groupApi.UpdateGroupHandlerFunc(func(params groupApi.UpdateGroupParams, session *models.Principal) middleware.Responder {
|
||||
groupUpdateResp, err := getUpdateGroupResponse(session, params)
|
||||
if err != nil {
|
||||
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, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
listGroupsResponse := &models.ListGroupsResponse{
|
||||
Groups: groups,
|
||||
Total: int64(len(groups)),
|
||||
}
|
||||
|
||||
return listGroupsResponse, nil
|
||||
}
|
||||
|
||||
// groupInfo calls MinIO server get Group's info
|
||||
func groupInfo(ctx context.Context, client MinioAdmin, group string) (*madmin.GroupDesc, error) {
|
||||
groupDesc, err := client.getGroupDescription(ctx, group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return groupDesc, nil
|
||||
}
|
||||
|
||||
// getGroupInfoResponse performs groupInfo() and serializes it to the handler's output
|
||||
func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoParams) (*models.Group, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groupName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
groupDesc, err := groupInfo(ctx, adminClient, groupName)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
groupResponse := &models.Group{
|
||||
Members: groupDesc.Members,
|
||||
Name: groupDesc.Name,
|
||||
Policy: groupDesc.Policy,
|
||||
Status: groupDesc.Status,
|
||||
}
|
||||
|
||||
return groupResponse, nil
|
||||
}
|
||||
|
||||
// addGroupAdd a MinIO group with the defined members
|
||||
func addGroup(ctx context.Context, client MinioAdmin, group string, members []string) error {
|
||||
gAddRemove := madmin.GroupAddRemove{
|
||||
Group: group,
|
||||
Members: members,
|
||||
IsRemove: false,
|
||||
}
|
||||
err := client.updateGroupMembers(ctx, gAddRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAddGroupResponse performs addGroup() and serializes it to the handler's output
|
||||
func getAddGroupResponse(session *models.Principal, params groupApi.AddGroupParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// AddGroup request needed to proceed
|
||||
if params.Body == nil {
|
||||
return ErrorWithContext(ctx, ErrGroupBodyNotInRequest)
|
||||
}
|
||||
groupRequest := params.Body
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
groupList, _ := adminClient.listGroups(ctx)
|
||||
|
||||
for _, b := range groupList {
|
||||
if b == *groupRequest.Group {
|
||||
return ErrorWithContext(ctx, ErrGroupAlreadyExists)
|
||||
}
|
||||
}
|
||||
|
||||
if err := addGroup(ctx, adminClient, *groupRequest.Group, groupRequest.Members); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeGroup deletes a minIO group only if it has no members
|
||||
func removeGroup(ctx context.Context, client MinioAdmin, group string) error {
|
||||
gAddRemove := madmin.GroupAddRemove{
|
||||
Group: group,
|
||||
Members: []string{},
|
||||
IsRemove: true,
|
||||
}
|
||||
err := client.updateGroupMembers(ctx, gAddRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRemoveGroupResponse performs removeGroup() and serializes it to the handler's output
|
||||
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(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// Create a MinIO Admin Client interface implementation
|
||||
// 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 {
|
||||
minioError := madmin.ToErrorResponse(err)
|
||||
err2 := ErrorWithContext(ctx, err)
|
||||
if minioError.Code == "XMinioAdminNoSuchGroup" {
|
||||
err2.Code = 404
|
||||
}
|
||||
return err2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateGroup updates a group by adding/removing members and setting the status to the desired one
|
||||
//
|
||||
// isRemove: whether remove members or not
|
||||
func updateGroupMembers(ctx context.Context, client MinioAdmin, group string, members []string, isRemove bool) error {
|
||||
gAddRemove := madmin.GroupAddRemove{
|
||||
Group: group,
|
||||
Members: members,
|
||||
IsRemove: isRemove,
|
||||
}
|
||||
err := client.updateGroupMembers(ctx, gAddRemove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addOrDeleteMembers updates a group members by adding or deleting them based on the expectedMembers
|
||||
func addOrDeleteMembers(ctx context.Context, client MinioAdmin, group *madmin.GroupDesc, expectedMembers []string) error {
|
||||
// get members to delete/add
|
||||
membersToDelete := DifferenceArrays(group.Members, expectedMembers)
|
||||
membersToAdd := DifferenceArrays(expectedMembers, group.Members)
|
||||
// delete members if any to be deleted
|
||||
if len(membersToDelete) > 0 {
|
||||
err := updateGroupMembers(ctx, client, group.Name, membersToDelete, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// add members if any to be added
|
||||
if len(membersToAdd) > 0 {
|
||||
err := updateGroupMembers(ctx, client, group.Name, membersToAdd, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setGroupStatus(ctx context.Context, client MinioAdmin, group, status string) error {
|
||||
var setStatus madmin.GroupStatus
|
||||
switch status {
|
||||
case "enabled":
|
||||
setStatus = madmin.GroupEnabled
|
||||
case "disabled":
|
||||
setStatus = madmin.GroupDisabled
|
||||
default:
|
||||
return errors.New(500, "status not valid")
|
||||
}
|
||||
return client.setGroupStatus(ctx, group, setStatus)
|
||||
}
|
||||
|
||||
// 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, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return nil, ErrorWithContext(ctx, ErrGroupNameNotInRequest)
|
||||
}
|
||||
if params.Body == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrGroupBodyNotInRequest)
|
||||
}
|
||||
expectedGroupUpdate := params.Body
|
||||
|
||||
groupName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
groupResponse := &models.Group{
|
||||
Name: groupUpdated.Name,
|
||||
Members: groupUpdated.Members,
|
||||
Policy: groupUpdated.Policy,
|
||||
Status: groupUpdated.Status,
|
||||
}
|
||||
return groupResponse, nil
|
||||
}
|
||||
|
||||
// groupUpdate updates a group given the expected parameters, compares the expected parameters against the current ones
|
||||
// and updates them accordingly, status is only updated if the expected status is different than the current one.
|
||||
// Then fetches the group again to return the object updated.
|
||||
func groupUpdate(ctx context.Context, client MinioAdmin, groupName string, expectedGroup *models.UpdateGroupRequest) (*madmin.GroupDesc, error) {
|
||||
expectedMembers := expectedGroup.Members
|
||||
expectedStatus := *expectedGroup.Status
|
||||
// get current members and status
|
||||
groupDescription, err := groupInfo(ctx, client, groupName)
|
||||
if err != nil {
|
||||
LogInfo("error getting group info: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// update group members
|
||||
err = addOrDeleteMembers(ctx, client, groupDescription, expectedMembers)
|
||||
if err != nil {
|
||||
LogInfo("error updating group: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// update group status only if different from current status
|
||||
if expectedStatus != groupDescription.Status {
|
||||
err = setGroupStatus(ctx, client, groupDescription.Name, expectedStatus)
|
||||
if err != nil {
|
||||
LogInfo("error updating group's status: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// return latest group info to verify that changes were applied correctly
|
||||
groupDescription, err = groupInfo(ctx, client, groupName)
|
||||
if err != nil {
|
||||
LogInfo("error getting group info: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return groupDescription, nil
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestListGroups(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : listGroups() Get response from minio client with two Groups and return the same number on listGroups()
|
||||
mockGroupsList := []string{"group1", "group2"}
|
||||
|
||||
// mock function response from listGroups()
|
||||
minioListGroupsMock = func() ([]string, error) {
|
||||
return mockGroupsList, nil
|
||||
}
|
||||
// get list Groups response this response should have Name, CreationDate, Size and Access
|
||||
// as part of of each Groups
|
||||
function := "listGroups()"
|
||||
groupsList, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// verify length of Groupss is correct
|
||||
assert.Equal(len(mockGroupsList), len(groupsList), fmt.Sprintf("Failed on %s: length of Groups's lists is not the same", function))
|
||||
|
||||
for i, g := range groupsList {
|
||||
assert.Equal(mockGroupsList[i], g)
|
||||
}
|
||||
|
||||
// Test-2 : listGroups() Return error and see that the error is handled correctly and returned
|
||||
minioListGroupsMock = func() ([]string, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
_, err = adminClient.listGroups(ctx)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : addGroup() add a new group with two members
|
||||
newGroup := "acmeGroup"
|
||||
groupMembers := []string{"user1", "user2"}
|
||||
// mock function response from updateGroupMembers()
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
function := "addGroup()"
|
||||
if err := addGroup(ctx, adminClient, newGroup, groupMembers); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : addGroup() Return error and see that the error is handled correctly and returned
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
if err := addGroup(ctx, adminClient, newGroup, groupMembers); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : removeGroup() remove group assume it has no members
|
||||
groupToRemove := "acmeGroup"
|
||||
// mock function response from updateGroupMembers()
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
function := "removeGroup()"
|
||||
if err := removeGroup(ctx, adminClient, groupToRemove); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : removeGroup() Return error and see that the error is handled correctly and returned
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := removeGroup(ctx, adminClient, groupToRemove); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : groupInfo() get group info
|
||||
groupName := "acmeGroup"
|
||||
mockResponse := &madmin.GroupDesc{
|
||||
Name: groupName,
|
||||
Policy: "policyTest",
|
||||
Members: []string{"user1", "user2"},
|
||||
Status: "enabled",
|
||||
}
|
||||
// mock function response from updateGroupMembers()
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
function := "groupInfo()"
|
||||
info, err := groupInfo(ctx, adminClient, groupName)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(groupName, info.Name)
|
||||
assert.Equal("policyTest", info.Policy)
|
||||
assert.ElementsMatch([]string{"user1", "user2"}, info.Members)
|
||||
assert.Equal("enabled", info.Status)
|
||||
|
||||
// Test-2 : groupInfo() Return error and see that the error is handled correctly and returned
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
_, err = groupInfo(ctx, adminClient, groupName)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateGroup(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : addOrDeleteMembers() update group members add user3 and delete user2
|
||||
function := "addOrDeleteMembers()"
|
||||
groupName := "acmeGroup"
|
||||
mockGroupDesc := &madmin.GroupDesc{
|
||||
Name: groupName,
|
||||
Policy: "policyTest",
|
||||
Members: []string{"user1", "user2"},
|
||||
Status: "enabled",
|
||||
}
|
||||
membersDesired := []string{"user3", "user1"}
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
if err := addOrDeleteMembers(ctx, adminClient, mockGroupDesc, membersDesired); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : addOrDeleteMembers() handle error correctly
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := addOrDeleteMembers(ctx, adminClient, mockGroupDesc, membersDesired); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3 : addOrDeleteMembers() only add members but handle error on adding
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
membersDesired = []string{"user3", "user1", "user2"}
|
||||
if err := addOrDeleteMembers(ctx, adminClient, mockGroupDesc, membersDesired); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-4: addOrDeleteMembers() no updates needed so error shall not be triggered or handled.
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
membersDesired = []string{"user1", "user2"}
|
||||
if err := addOrDeleteMembers(ctx, adminClient, mockGroupDesc, membersDesired); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-5 : groupUpdate() integrate all from getting current group to update it and see if it changed.
|
||||
// This test mocks one function twice and makes sure it returns different content on each call.
|
||||
function = "groupUpdate()"
|
||||
groupName = "acmeGroup"
|
||||
membersDesired = []string{"user1", "user2", "user3"}
|
||||
expectedGroupUpdate := &models.UpdateGroupRequest{
|
||||
Members: membersDesired,
|
||||
Status: swag.String("disabled"),
|
||||
}
|
||||
mockResponseBeforeUpdate := &madmin.GroupDesc{
|
||||
Name: groupName,
|
||||
Policy: "policyTest",
|
||||
Members: []string{"user1", "user2"},
|
||||
Status: "enabled",
|
||||
}
|
||||
mockResponseAfterUpdate := &madmin.GroupDesc{
|
||||
Name: groupName,
|
||||
Policy: "policyTest",
|
||||
Members: []string{"user1", "user2", "user3"},
|
||||
Status: "disabled",
|
||||
}
|
||||
// groupUpdate uses getInfo() twice which uses getGroupDescription() so we need to mock as if it called
|
||||
// the function twice but the second time returned an error
|
||||
is2ndRunGroupInfo := false
|
||||
// mock function response from updateGroupMembers()
|
||||
minioGetGroupDescriptionMock = func(_ string) (*madmin.GroupDesc, error) {
|
||||
if is2ndRunGroupInfo {
|
||||
return mockResponseAfterUpdate, nil
|
||||
}
|
||||
is2ndRunGroupInfo = true
|
||||
return mockResponseBeforeUpdate, nil
|
||||
}
|
||||
minioUpdateGroupMembersMock = func(madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// assert elements were updated as expected
|
||||
assert.ElementsMatch(membersDesired, groupUpdated.Members)
|
||||
assert.Equal(groupName, groupUpdated.Name)
|
||||
assert.Equal(*expectedGroupUpdate.Status, groupUpdated.Status)
|
||||
}
|
||||
|
||||
func TestSetGroupStatus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setGroupStatus()"
|
||||
groupName := "acmeGroup"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: setGroupStatus() update valid disabled status
|
||||
expectedStatus := "disabled"
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-2: setGroupStatus() update valid enabled status
|
||||
expectedStatus = "enabled"
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-3: setGroupStatus() update invalid status, should send error
|
||||
expectedStatus = "invalid"
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); assert.Error(err) {
|
||||
assert.Equal("status not valid", err.Error())
|
||||
}
|
||||
// Test-4: setGroupStatus() handler error correctly
|
||||
expectedStatus = "enabled"
|
||||
minioSetGroupStatusMock = func(_ string, _ madmin.GroupStatus) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := setGroupStatus(ctx, adminClient, groupName, expectedStatus); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
subnet "github.com/minio/console/pkg/subnet"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
// startHealthInfo starts fetching mc.ServerHealthInfo and
|
||||
// sends messages with the corresponding data on the websocket connection
|
||||
func startHealthInfo(ctx context.Context, conn WSConn, client MinioAdmin, deadline *time.Duration) error {
|
||||
if deadline == nil {
|
||||
return errors.New("duration can't be nil on startHealthInfo")
|
||||
}
|
||||
|
||||
// Fetch info of all servers (cluster or single server)
|
||||
healthDataTypes := []madmin.HealthDataType{
|
||||
madmin.HealthDataTypeMinioInfo,
|
||||
madmin.HealthDataTypeMinioConfig,
|
||||
madmin.HealthDataTypeSysCPU,
|
||||
madmin.HealthDataTypeSysDriveHw,
|
||||
madmin.HealthDataTypeSysDocker,
|
||||
madmin.HealthDataTypeSysOsInfo,
|
||||
madmin.HealthDataTypeSysLoad,
|
||||
madmin.HealthDataTypeSysMem,
|
||||
madmin.HealthDataTypeSysNet,
|
||||
madmin.HealthDataTypeSysProcess,
|
||||
}
|
||||
var err error
|
||||
// Fetch info of all servers (cluster or single server)
|
||||
healthInfo, version, err := client.serverHealthInfo(ctx, healthDataTypes, *deadline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compressedDiag, err := mc.TarGZHealthInfo(healthInfo, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encodedDiag := b64.StdEncoding.EncodeToString(compressedDiag)
|
||||
type messageReport struct {
|
||||
Encoded string `json:"encoded"`
|
||||
ServerHealthInfo interface{} `json:"serverHealthInfo"`
|
||||
SubnetResponse string `json:"subnetResponse"`
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, conn.remoteAddress())
|
||||
err = sendHealthInfoToSubnet(ctx, healthInfo, client)
|
||||
report := messageReport{
|
||||
Encoded: encodedDiag,
|
||||
ServerHealthInfo: healthInfo,
|
||||
SubnetResponse: mc.SubnetBaseURL() + "/health",
|
||||
}
|
||||
if err != nil {
|
||||
report.SubnetResponse = fmt.Sprintf("Error: %s", err.Error())
|
||||
}
|
||||
|
||||
message, err := json.Marshal(report)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send Message through websocket connection
|
||||
return conn.writeMessage(websocket.TextMessage, message)
|
||||
}
|
||||
|
||||
// getHealthInfoOptionsFromReq gets duration for startHealthInfo request
|
||||
// path come as : `/health-info?deadline=2h`
|
||||
func getHealthInfoOptionsFromReq(req *http.Request) (*time.Duration, error) {
|
||||
deadlineDuration, err := time.ParseDuration(req.FormValue("deadline"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &deadlineDuration, nil
|
||||
}
|
||||
|
||||
func updateMcGlobals(subnetTokenConfig subnet.LicenseTokenConfig) error {
|
||||
mc.GlobalDevMode = getConsoleDevMode()
|
||||
if len(subnetTokenConfig.Proxy) > 0 {
|
||||
proxyURL, e := url.Parse(subnetTokenConfig.Proxy)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
mc.GlobalSubnetProxyURL = proxyURL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendHealthInfoToSubnet(ctx context.Context, healthInfo interface{}, client MinioAdmin) error {
|
||||
filename := fmt.Sprintf("health_%d.json.gz", time.Now().Unix())
|
||||
subnetTokenConfig, e := GetSubnetKeyFromMinIOConfig(ctx, client)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = updateMcGlobals(*subnetTokenConfig)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
var apiKey string
|
||||
if len(subnetTokenConfig.APIKey) != 0 {
|
||||
apiKey = subnetTokenConfig.APIKey
|
||||
} else {
|
||||
apiKey, e = subnet.GetSubnetAPIKeyUsingLicense(subnetTokenConfig.License)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
compressedHealthInfo, e := mc.TarGZHealthInfo(healthInfo, madmin.HealthInfoVersion)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = os.WriteFile(filename, compressedHealthInfo, 0o666)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
headers := mc.SubnetAPIKeyAuthHeaders(apiKey)
|
||||
resp, e := (&mc.SubnetFileUploader{
|
||||
FilePath: filename,
|
||||
ReqURL: mc.SubnetUploadURL("health"),
|
||||
Headers: headers,
|
||||
DeleteAfterUpload: true,
|
||||
}).UploadFileToSubnet()
|
||||
if e != nil {
|
||||
// file gets deleted only if upload is successful
|
||||
// so we delete explicitly here as we already have the bytes
|
||||
logger.LogIf(ctx, os.Remove(filename))
|
||||
return e
|
||||
}
|
||||
|
||||
type SubnetResponse struct {
|
||||
LicenseV2 string `json:"license_v2,omitempty"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
}
|
||||
|
||||
var subnetResp SubnetResponse
|
||||
e = json.Unmarshal([]byte(resp), &subnetResp)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
madmin "github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func Test_serverHealthInfo(t *testing.T) {
|
||||
var testReceiver chan madmin.HealthInfo
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
client := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
deadlineDuration, _ := time.ParseDuration("1h")
|
||||
|
||||
type args struct {
|
||||
deadline time.Duration
|
||||
wsWriteMock func(messageType int, data []byte) error
|
||||
mockMessages []madmin.HealthInfo
|
||||
}
|
||||
tests := []struct {
|
||||
test string
|
||||
args args
|
||||
wantError error
|
||||
}{
|
||||
{
|
||||
test: "Return simple health info, no errors",
|
||||
args: args{
|
||||
deadline: deadlineDuration,
|
||||
mockMessages: []madmin.HealthInfo{{}, {}},
|
||||
wsWriteMock: func(_ int, data []byte) error {
|
||||
// mock connection WriteMessage() no error
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.HealthInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
testReceiver <- t
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
test: "Return simple health info2, no errors",
|
||||
args: args{
|
||||
deadline: deadlineDuration,
|
||||
mockMessages: []madmin.HealthInfo{{}},
|
||||
wsWriteMock: func(_ int, data []byte) error {
|
||||
// mock connection WriteMessage() no error
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.HealthInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
testReceiver <- t
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
test: "Handle error on ws write",
|
||||
args: args{
|
||||
deadline: deadlineDuration,
|
||||
mockMessages: []madmin.HealthInfo{{}},
|
||||
wsWriteMock: func(_ int, data []byte) error {
|
||||
// mock connection WriteMessage() no error
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.HealthInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
return errors.New("error on write")
|
||||
},
|
||||
},
|
||||
wantError: errors.New("error on write"),
|
||||
},
|
||||
{
|
||||
test: "Handle error on health function",
|
||||
args: args{
|
||||
deadline: deadlineDuration,
|
||||
mockMessages: []madmin.HealthInfo{
|
||||
{
|
||||
Error: "error on healthInfo",
|
||||
},
|
||||
},
|
||||
wsWriteMock: func(_ int, data []byte) error {
|
||||
// mock connection WriteMessage() no error
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.HealthInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.test, func(_ *testing.T) {
|
||||
// make testReceiver channel
|
||||
testReceiver = make(chan madmin.HealthInfo, len(tt.args.mockMessages))
|
||||
// mock function same for all tests, changes mockMessages
|
||||
minioServerHealthInfoMock = func(_ context.Context, _ []madmin.HealthDataType,
|
||||
_ time.Duration,
|
||||
) (interface{}, string, error) {
|
||||
info := tt.args.mockMessages[0]
|
||||
return info, madmin.HealthInfoVersion, nil
|
||||
}
|
||||
connWriteMessageMock = tt.args.wsWriteMock
|
||||
err := startHealthInfo(ctx, mockWSConn, client, &deadlineDuration)
|
||||
// close test mock channel
|
||||
close(testReceiver)
|
||||
// check that the TestReceiver got the same number of data from Console.
|
||||
index := 0
|
||||
for info := range testReceiver {
|
||||
if !reflect.DeepEqual(info, tt.args.mockMessages[index]) {
|
||||
t.Errorf("startHealthInfo() got: %v, want: %v", info, tt.args.mockMessages[index])
|
||||
return
|
||||
}
|
||||
index++
|
||||
}
|
||||
if !reflect.DeepEqual(err, tt.wantError) {
|
||||
t.Errorf("startHealthInfo() error: %v, wantError: %v", err, tt.wantError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
290
api/admin_idp.go
290
api/admin_idp.go
@@ -1,290 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/api/operations/idp"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
var errInvalidIDPType = fmt.Errorf("IDP type must be one of %v", madmin.ValidIDPConfigTypes)
|
||||
|
||||
func registerIDPHandlers(api *operations.ConsoleAPI) {
|
||||
api.IdpCreateConfigurationHandler = idp.CreateConfigurationHandlerFunc(func(params idp.CreateConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := createIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewCreateConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewCreateConfigurationCreated().WithPayload(response)
|
||||
})
|
||||
api.IdpUpdateConfigurationHandler = idp.UpdateConfigurationHandlerFunc(func(params idp.UpdateConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := updateIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewUpdateConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewUpdateConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpListConfigurationsHandler = idp.ListConfigurationsHandlerFunc(func(params idp.ListConfigurationsParams, session *models.Principal) middleware.Responder {
|
||||
response, err := listIDPConfigurationsResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewListConfigurationsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewListConfigurationsOK().WithPayload(response)
|
||||
})
|
||||
api.IdpDeleteConfigurationHandler = idp.DeleteConfigurationHandlerFunc(func(params idp.DeleteConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := deleteIDPConfigurationResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewDeleteConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewDeleteConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpGetConfigurationHandler = idp.GetConfigurationHandlerFunc(func(params idp.GetConfigurationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getIDPConfigurationsResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewGetConfigurationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewGetConfigurationOK().WithPayload(response)
|
||||
})
|
||||
api.IdpGetLDAPEntitiesHandler = idp.GetLDAPEntitiesHandlerFunc(func(params idp.GetLDAPEntitiesParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getLDAPEntitiesResponse(session, params)
|
||||
if err != nil {
|
||||
return idp.NewGetLDAPEntitiesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return idp.NewGetLDAPEntitiesOK().WithPayload(response)
|
||||
})
|
||||
}
|
||||
|
||||
func createIDPConfigurationResponse(session *models.Principal, params idp.CreateConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := createOrUpdateIDPConfig(ctx, params.Type, params.Body.Name, params.Body.Input, false, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func updateIDPConfigurationResponse(session *models.Principal, params idp.UpdateConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := createOrUpdateIDPConfig(ctx, params.Type, params.Name, params.Body.Input, true, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func createOrUpdateIDPConfig(ctx context.Context, idpType, name, input string, update bool, client MinioAdmin) (bool, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return false, errInvalidIDPType
|
||||
}
|
||||
restart, err := client.addOrUpdateIDPConfig(ctx, idpType, name, input, update)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func listIDPConfigurationsResponse(session *models.Principal, params idp.ListConfigurationsParams) (*models.IdpListConfigurationsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
results, err := listIDPConfigurations(ctx, params.Type, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.IdpListConfigurationsResponse{Results: results}, nil
|
||||
}
|
||||
|
||||
func listIDPConfigurations(ctx context.Context, idpType string, client MinioAdmin) ([]*models.IdpServerConfiguration, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return nil, errInvalidIDPType
|
||||
}
|
||||
results, err := client.listIDPConfig(ctx, idpType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseIDPConfigurations(results), nil
|
||||
}
|
||||
|
||||
func parseIDPConfigurations(configs []madmin.IDPListItem) (serverConfigs []*models.IdpServerConfiguration) {
|
||||
for _, c := range configs {
|
||||
serverConfigs = append(serverConfigs, &models.IdpServerConfiguration{
|
||||
Name: c.Name,
|
||||
Enabled: c.Enabled,
|
||||
Type: c.Type,
|
||||
})
|
||||
}
|
||||
return serverConfigs
|
||||
}
|
||||
|
||||
func deleteIDPConfigurationResponse(session *models.Principal, params idp.DeleteConfigurationParams) (*models.SetIDPResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
restart, err := deleteIDPConfig(ctx, params.Type, params.Name, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetIDPResponse{Restart: restart}, nil
|
||||
}
|
||||
|
||||
func deleteIDPConfig(ctx context.Context, idpType, name string, client MinioAdmin) (bool, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return false, errInvalidIDPType
|
||||
}
|
||||
restart, err := client.deleteIDPConfig(ctx, idpType, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return restart, nil
|
||||
}
|
||||
|
||||
func getIDPConfigurationsResponse(session *models.Principal, params idp.GetConfigurationParams) (*models.IdpServerConfiguration, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
result, err := getIDPConfiguration(ctx, params.Type, params.Name, AdminClient{Client: mAdmin})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getIDPConfiguration(ctx context.Context, idpType, name string, client MinioAdmin) (*models.IdpServerConfiguration, error) {
|
||||
if !madmin.ValidIDPConfigTypes.Contains(idpType) {
|
||||
return nil, errInvalidIDPType
|
||||
}
|
||||
config, err := client.getIDPConfig(ctx, idpType, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.IdpServerConfiguration{
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
Info: parseIDPConfigurationsInfo(config.Info),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseIDPConfigurationsInfo(infoList []madmin.IDPCfgInfo) (results []*models.IdpServerConfigurationInfo) {
|
||||
for _, info := range infoList {
|
||||
results = append(results, &models.IdpServerConfigurationInfo{
|
||||
Key: info.Key,
|
||||
Value: info.Value,
|
||||
IsCfg: info.IsCfg,
|
||||
IsEnv: info.IsEnv,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getLDAPEntitiesResponse(session *models.Principal, params idp.GetLDAPEntitiesParams) (*models.LdapEntities, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
result, err := getEntitiesResult(ctx, AdminClient{Client: mAdmin}, params.Body.Users, params.Body.Groups, params.Body.Policies)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getEntitiesResult(ctx context.Context, client MinioAdmin, users, groups, policies []string) (*models.LdapEntities, error) {
|
||||
entities, err := client.getLDAPPolicyEntities(ctx, madmin.PolicyEntitiesQuery{
|
||||
Users: users,
|
||||
Groups: groups,
|
||||
Policy: policies,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result models.LdapEntities
|
||||
|
||||
var usersEntity []*models.LdapUserPolicyEntity
|
||||
var groupsEntity []*models.LdapGroupPolicyEntity
|
||||
var policiesEntity []*models.LdapPolicyEntity
|
||||
|
||||
result.Timestamp = entities.Timestamp.Format(time.RFC3339)
|
||||
|
||||
for _, userMapping := range entities.UserMappings {
|
||||
mapItem := models.LdapUserPolicyEntity{
|
||||
User: userMapping.User,
|
||||
Policies: userMapping.Policies,
|
||||
}
|
||||
|
||||
usersEntity = append(usersEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Users = usersEntity
|
||||
|
||||
for _, groupsMapping := range entities.GroupMappings {
|
||||
mapItem := models.LdapGroupPolicyEntity{
|
||||
Group: groupsMapping.Group,
|
||||
Policies: groupsMapping.Policies,
|
||||
}
|
||||
|
||||
groupsEntity = append(groupsEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Groups = groupsEntity
|
||||
|
||||
for _, policyMapping := range entities.PolicyMappings {
|
||||
mapItem := models.LdapPolicyEntity{
|
||||
Policy: policyMapping.Policy,
|
||||
Users: policyMapping.Users,
|
||||
Groups: policyMapping.Groups,
|
||||
}
|
||||
|
||||
policiesEntity = append(policiesEntity, &mapItem)
|
||||
}
|
||||
|
||||
result.Policies = policiesEntity
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/api/operations/idp"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type IDPTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestRegisterIDPHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerIDPHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.IdpCreateConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpListConfigurationsHandler)
|
||||
suite.assert.Nil(api.IdpUpdateConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.Nil(api.IdpDeleteConfigurationHandler)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.IdpCreateConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpListConfigurationsHandler)
|
||||
suite.assert.NotNil(api.IdpUpdateConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpGetConfigurationHandler)
|
||||
suite.assert.NotNil(api.IdpDeleteConfigurationHandler)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initCreateIDPConfigurationRequest()
|
||||
response := api.IdpCreateConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.CreateConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initCreateIDPConfigurationRequest() (params idp.CreateConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.IdpServerConfiguration{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "ldap", "", "", false, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestCreateIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "", "", "", false, suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initUpdateIDPConfigurationRequest()
|
||||
response := api.IdpUpdateConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.UpdateConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initUpdateIDPConfigurationRequest() (params idp.UpdateConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.IdpServerConfiguration{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "ldap", "", "", true, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestUpdateIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := createOrUpdateIDPConfig(ctx, "", "", "", true, suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initListIDPConfigurationsRequest()
|
||||
response := api.IdpListConfigurationsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.ListConfigurationsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initListIDPConfigurationsRequest() (params idp.ListConfigurationsParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationsWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := listIDPConfigurations(ctx, "ldap", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestListIDPConfigurationsWithWrongType() {
|
||||
ctx := context.Background()
|
||||
res, err := listIDPConfigurations(ctx, "", suite.adminClient)
|
||||
suite.assert.Nil(res)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initDeleteIDPConfigurationRequest()
|
||||
response := api.IdpDeleteConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.DeleteConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initDeleteIDPConfigurationRequest() (params idp.DeleteConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
_, err := deleteIDPConfig(ctx, "ldap", "", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestDeleteIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
_, err := deleteIDPConfig(ctx, "", "", suite.adminClient)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationHandlerWithError() {
|
||||
params, api := suite.initGetIDPConfigurationRequest()
|
||||
response := api.IdpGetConfigurationHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*idp.GetConfigurationDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) initGetIDPConfigurationRequest() (params idp.GetConfigurationParams, api operations.ConsoleAPI) {
|
||||
registerIDPHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Type = "ldap"
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := getIDPConfiguration(ctx, "ldap", "", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *IDPTestSuite) TestGetIDPConfigurationWithWrongType() {
|
||||
ctx := context.Background()
|
||||
res, err := getIDPConfiguration(ctx, "", "", suite.adminClient)
|
||||
suite.assert.Nil(res)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func TestIDP(t *testing.T) {
|
||||
suite.Run(t, new(IDPTestSuite))
|
||||
}
|
||||
|
||||
func TestGetEntitiesResult(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
client := AdminClientMock{}
|
||||
function := "getEntitiesResult()"
|
||||
|
||||
usersList := []string{"user1", "user2", "user3"}
|
||||
policiesList := []string{"policy1", "policy2", "policy3"}
|
||||
groupsList := []string{"group1", "group3", "group5"}
|
||||
|
||||
policyMap := []madmin.PolicyEntities{
|
||||
{Policy: "testPolicy0", Groups: groupsList, Users: usersList},
|
||||
{Policy: "testPolicy1", Groups: groupsList, Users: usersList},
|
||||
}
|
||||
|
||||
usersMap := []madmin.UserPolicyEntities{
|
||||
{User: "testUser0", Policies: policiesList},
|
||||
{User: "testUser1", Policies: policiesList},
|
||||
}
|
||||
|
||||
groupsMap := []madmin.GroupPolicyEntities{
|
||||
{Group: "group0", Policies: policiesList},
|
||||
{Group: "group1", Policies: policiesList},
|
||||
}
|
||||
|
||||
// Test-1: getEntitiesResult list all information provided
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
mockResponse := madmin.PolicyEntitiesResult{
|
||||
PolicyMappings: policyMap,
|
||||
GroupMappings: groupsMap,
|
||||
UserMappings: usersMap,
|
||||
}
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
|
||||
entities, err := getEntitiesResult(ctx, client, usersList, groupsList, policiesList)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
for i, groupIt := range entities.Groups {
|
||||
assert.Equal(fmt.Sprintf("group%d", i), groupIt.Group)
|
||||
|
||||
for i, polItm := range groupIt.Policies {
|
||||
assert.Equal(policiesList[i], polItm)
|
||||
}
|
||||
}
|
||||
|
||||
for i, usrIt := range entities.Users {
|
||||
assert.Equal(fmt.Sprintf("testUser%d", i), usrIt.User)
|
||||
|
||||
for i, polItm := range usrIt.Policies {
|
||||
assert.Equal(policiesList[i], polItm)
|
||||
}
|
||||
}
|
||||
|
||||
for i, policyIt := range entities.Policies {
|
||||
assert.Equal(fmt.Sprintf("testPolicy%d", i), policyIt.Policy)
|
||||
|
||||
for i, userItm := range policyIt.Users {
|
||||
assert.Equal(usersList[i], userItm)
|
||||
}
|
||||
|
||||
for i, grItm := range policyIt.Groups {
|
||||
assert.Equal(groupsList[i], grItm)
|
||||
}
|
||||
}
|
||||
|
||||
// Test-2: getEntitiesResult error is returned from getLDAPPolicyEntities()
|
||||
minioGetLDAPPolicyEntitiesMock = func(_ context.Context, _ madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return madmin.PolicyEntitiesResult{}, errors.New("error")
|
||||
}
|
||||
|
||||
_, err = getEntitiesResult(ctx, client, usersList, groupsList, policiesList)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
1192
api/admin_info.go
1192
api/admin_info.go
File diff suppressed because it is too large
Load Diff
@@ -1,152 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
systemApi "github.com/minio/console/api/operations/system"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type AdminInfoTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
isPrometheusRequest bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{
|
||||
Servers: []madmin.ServerProperties{{
|
||||
Disks: []madmin.Disk{{}},
|
||||
}},
|
||||
Backend: madmin.ErasureBackend{Type: "mock"},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
if suite.isPrometheusRequest {
|
||||
w.WriteHeader(200)
|
||||
} else {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestRegisterAdminInfoHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerAdminInfoHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.SystemAdminInfoHandler)
|
||||
suite.assert.Nil(api.SystemDashboardWidgetDetailsHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.SystemAdminInfoHandler)
|
||||
suite.assert.NotNil(api.SystemDashboardWidgetDetailsHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestSystemAdminInfoHandlerWithError() {
|
||||
params, api := suite.initSystemAdminInfoRequest()
|
||||
response := api.SystemAdminInfoHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*systemApi.AdminInfoDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) initSystemAdminInfoRequest() (params systemApi.AdminInfoParams, api operations.ConsoleAPI) {
|
||||
registerAdminInfoHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
defaultOnly := false
|
||||
params.DefaultOnly = &defaultOnly
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestSystemDashboardWidgetDetailsHandlerWithError() {
|
||||
params, api := suite.initSystemDashboardWidgetDetailsRequest()
|
||||
response := api.SystemDashboardWidgetDetailsHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*systemApi.DashboardWidgetDetailsDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) initSystemDashboardWidgetDetailsRequest() (params systemApi.DashboardWidgetDetailsParams, api operations.ConsoleAPI) {
|
||||
registerAdminInfoHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetUsageWidgetsForDeploymentWithoutError() {
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
suite.isPrometheusRequest = true
|
||||
res, err := getUsageWidgetsForDeployment(ctx, suite.server.URL, suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.isPrometheusRequest = false
|
||||
}
|
||||
|
||||
func (suite *AdminInfoTestSuite) TestGetWidgetDetailsWithoutError() {
|
||||
ctx := context.WithValue(context.Background(), utils.ContextClientIP, "127.0.0.1")
|
||||
suite.isPrometheusRequest = true
|
||||
var step int32 = 1
|
||||
var start int64
|
||||
var end int64 = 1
|
||||
res, err := getWidgetDetails(ctx, suite.server.URL, "mock", 1, &step, &start, &end)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotNil(res)
|
||||
suite.isPrometheusRequest = false
|
||||
}
|
||||
|
||||
func TestAdminInfo(t *testing.T) {
|
||||
suite.Run(t, new(AdminInfoTestSuite))
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"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/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(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return middleware.ResponderFunc(processInspectResponse(¶ms, k, r))
|
||||
})
|
||||
}
|
||||
|
||||
func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) ([]byte, io.ReadCloser, *CodedAPIError) {
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
cfg := madmin.InspectOptions{
|
||||
File: params.File,
|
||||
Volume: params.Volume,
|
||||
}
|
||||
|
||||
// TODO: Remove encryption option and always encrypt.
|
||||
// Maybe also add public key field.
|
||||
if params.Encrypt != nil && *params.Encrypt {
|
||||
cfg.PublicKey, _ = base64.StdEncoding.DecodeString("MIIBCgKCAQEAs/128UFS9A8YSJY1XqYKt06dLVQQCGDee69T+0Tip/1jGAB4z0/3QMpH0MiS8Wjs4BRWV51qvkfAHzwwdU7y6jxU05ctb/H/WzRj3FYdhhHKdzear9TLJftlTs+xwj2XaADjbLXCV1jGLS889A7f7z5DgABlVZMQd9BjVAR8ED3xRJ2/ZCNuQVJ+A8r7TYPGMY3wWvhhPgPk3Lx4WDZxDiDNlFs4GQSaESSsiVTb9vyGe/94CsCTM6Cw9QG6ifHKCa/rFszPYdKCabAfHcS3eTr0GM+TThSsxO7KfuscbmLJkfQev1srfL2Ii2RbnysqIJVWKEwdW05ID8ryPkuTuwIDAQAB")
|
||||
}
|
||||
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
k, r, err := adminClient.inspect(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return k, r, nil
|
||||
}
|
||||
|
||||
// borrowed from mc cli
|
||||
func decryptInspectV1(key [32]byte, r io.Reader) io.ReadCloser {
|
||||
stream, err := sio.AES_256_GCM.Stream(key[:])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
nonce := make([]byte, stream.NonceSize())
|
||||
return io.NopCloser(stream.DecryptReader(r, nonce, nil))
|
||||
}
|
||||
|
||||
func processInspectResponse(params *inspectApi.InspectParams, k []byte, r io.ReadCloser) func(w http.ResponseWriter, _ runtime.Producer) {
|
||||
isEnc := params.Encrypt != nil && *params.Encrypt
|
||||
return func(w http.ResponseWriter, _ runtime.Producer) {
|
||||
ext := "enc"
|
||||
if len(k) == 32 && !isEnc {
|
||||
ext = "zip"
|
||||
r = decryptInspectV1(*(*[32]byte)(k), r)
|
||||
}
|
||||
fileName := fmt.Sprintf("inspect-%s-%s.%s", params.Volume, params.File, ext)
|
||||
fileName = strings.Map(func(r rune) rune {
|
||||
switch {
|
||||
case r >= 'A' && r <= 'Z':
|
||||
return r
|
||||
case r >= 'a' && r <= 'z':
|
||||
return r
|
||||
case r >= '0' && r <= '9':
|
||||
return r
|
||||
default:
|
||||
if strings.ContainsAny(string(r), "-+._") {
|
||||
return r
|
||||
}
|
||||
return '_'
|
||||
}
|
||||
}, fileName)
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
||||
|
||||
_, err := io.Copy(w, r)
|
||||
if err != nil {
|
||||
LogError("unable to write all the data: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
669
api/admin_kms.go
669
api/admin_kms.go
@@ -1,669 +0,0 @@
|
||||
// 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"
|
||||
"encoding/json"
|
||||
"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)
|
||||
registerKMSPolicyHandlers(api)
|
||||
registerKMSIdentityHandlers(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.KmsKMSImportKeyHandler = kmsAPI.KMSImportKeyHandlerFunc(func(params kmsAPI.KMSImportKeyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSImportKeyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSImportKeyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSImportKeyCreated()
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
api.KmsKMSDeleteKeyHandler = kmsAPI.KMSDeleteKeyHandlerFunc(func(params kmsAPI.KMSDeleteKeyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSDeleteKeyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDeleteKeyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDeleteKeyOK()
|
||||
})
|
||||
}
|
||||
|
||||
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 GetKMSImportKeyResponse(session *models.Principal, params kmsAPI.KMSImportKeyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
bytes, err := json.Marshal(params.Body)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return importKey(ctx, params.Name, bytes, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func importKey(ctx context.Context, key string, bytes []byte, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.importKey(ctx, key, bytes); 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
|
||||
}
|
||||
|
||||
func parseKeys(results []madmin.KMSKeyInfo) (data []*models.KmsKeyInfo) {
|
||||
for _, key := range results {
|
||||
data = append(data, &models.KmsKeyInfo{
|
||||
CreatedAt: key.CreatedAt,
|
||||
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
|
||||
}
|
||||
|
||||
func GetKMSDeleteKeyResponse(session *models.Principal, params kmsAPI.KMSDeleteKeyParams) *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 deleteKey(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func deleteKey(ctx context.Context, key string, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.deleteKey(ctx, key); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerKMSPolicyHandlers(api *operations.ConsoleAPI) {
|
||||
api.KmsKMSSetPolicyHandler = kmsAPI.KMSSetPolicyHandlerFunc(func(params kmsAPI.KMSSetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSSetPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSSetPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSSetPolicyOK()
|
||||
})
|
||||
|
||||
api.KmsKMSAssignPolicyHandler = kmsAPI.KMSAssignPolicyHandlerFunc(func(params kmsAPI.KMSAssignPolicyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSAssignPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSAssignPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSAssignPolicyOK()
|
||||
})
|
||||
|
||||
api.KmsKMSDescribePolicyHandler = kmsAPI.KMSDescribePolicyHandlerFunc(func(params kmsAPI.KMSDescribePolicyParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSDescribePolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDescribePolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDescribePolicyOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSGetPolicyHandler = kmsAPI.KMSGetPolicyHandlerFunc(func(params kmsAPI.KMSGetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSGetPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSGetPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSGetPolicyOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSListPoliciesHandler = kmsAPI.KMSListPoliciesHandlerFunc(func(params kmsAPI.KMSListPoliciesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSListPoliciesResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSListPoliciesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSListPoliciesOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSDeletePolicyHandler = kmsAPI.KMSDeletePolicyHandlerFunc(func(params kmsAPI.KMSDeletePolicyParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSDeletePolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDeletePolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDeletePolicyOK()
|
||||
})
|
||||
}
|
||||
|
||||
func GetKMSSetPolicyResponse(session *models.Principal, params kmsAPI.KMSSetPolicyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
bytes, err := json.Marshal(params.Body)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return setPolicy(ctx, *params.Body.Policy, bytes, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func setPolicy(ctx context.Context, policy string, content []byte, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.setKMSPolicy(ctx, policy, content); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetKMSAssignPolicyResponse(session *models.Principal, params kmsAPI.KMSAssignPolicyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
bytes, err := json.Marshal(params.Body)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return assignPolicy(ctx, params.Name, bytes, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func assignPolicy(ctx context.Context, policy string, content []byte, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.assignPolicy(ctx, policy, content); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetKMSDescribePolicyResponse(session *models.Principal, params kmsAPI.KMSDescribePolicyParams) (*models.KmsDescribePolicyResponse, *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 describePolicy(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func describePolicy(ctx context.Context, policy string, minioClient MinioAdmin) (*models.KmsDescribePolicyResponse, *CodedAPIError) {
|
||||
dp, err := minioClient.describePolicy(ctx, policy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsDescribePolicyResponse{
|
||||
Name: dp.Name,
|
||||
CreatedAt: dp.CreatedAt,
|
||||
CreatedBy: dp.CreatedBy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetKMSGetPolicyResponse(session *models.Principal, params kmsAPI.KMSGetPolicyParams) (*models.KmsGetPolicyResponse, *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 getPolicy(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func getPolicy(ctx context.Context, policy string, minioClient MinioAdmin) (*models.KmsGetPolicyResponse, *CodedAPIError) {
|
||||
p, err := minioClient.getKMSPolicy(ctx, policy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsGetPolicyResponse{
|
||||
Allow: p.Allow,
|
||||
Deny: p.Deny,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetKMSListPoliciesResponse(session *models.Principal, params kmsAPI.KMSListPoliciesParams) (*models.KmsListPoliciesResponse, *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 listKMSPolicies(ctx, pattern, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func listKMSPolicies(ctx context.Context, pattern string, minioClient MinioAdmin) (*models.KmsListPoliciesResponse, *CodedAPIError) {
|
||||
results, err := minioClient.listKMSPolicies(ctx, pattern)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsListPoliciesResponse{Results: parsePolicies(results)}, nil
|
||||
}
|
||||
|
||||
func parsePolicies(results []madmin.KMSPolicyInfo) (data []*models.KmsPolicyInfo) {
|
||||
for _, policy := range results {
|
||||
data = append(data, &models.KmsPolicyInfo{
|
||||
CreatedAt: policy.CreatedAt,
|
||||
CreatedBy: policy.CreatedBy,
|
||||
Name: policy.Name,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func GetKMSDeletePolicyResponse(session *models.Principal, params kmsAPI.KMSDeletePolicyParams) *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 deletePolicy(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func deletePolicy(ctx context.Context, policy string, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.deletePolicy(ctx, policy); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerKMSIdentityHandlers(api *operations.ConsoleAPI) {
|
||||
api.KmsKMSDescribeIdentityHandler = kmsAPI.KMSDescribeIdentityHandlerFunc(func(params kmsAPI.KMSDescribeIdentityParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSDescribeIdentityResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDescribeIdentityDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDescribeIdentityOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSDescribeSelfIdentityHandler = kmsAPI.KMSDescribeSelfIdentityHandlerFunc(func(params kmsAPI.KMSDescribeSelfIdentityParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSDescribeSelfIdentityResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDescribeSelfIdentityDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDescribeSelfIdentityOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.KmsKMSListIdentitiesHandler = kmsAPI.KMSListIdentitiesHandlerFunc(func(params kmsAPI.KMSListIdentitiesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetKMSListIdentitiesResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSListIdentitiesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSListIdentitiesOK().WithPayload(resp)
|
||||
})
|
||||
api.KmsKMSDeleteIdentityHandler = kmsAPI.KMSDeleteIdentityHandlerFunc(func(params kmsAPI.KMSDeleteIdentityParams, session *models.Principal) middleware.Responder {
|
||||
err := GetKMSDeleteIdentityResponse(session, params)
|
||||
if err != nil {
|
||||
return kmsAPI.NewKMSDeleteIdentityDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return kmsAPI.NewKMSDeleteIdentityOK()
|
||||
})
|
||||
}
|
||||
|
||||
func GetKMSDescribeIdentityResponse(session *models.Principal, params kmsAPI.KMSDescribeIdentityParams) (*models.KmsDescribeIdentityResponse, *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 describeIdentity(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func describeIdentity(ctx context.Context, identity string, minioClient MinioAdmin) (*models.KmsDescribeIdentityResponse, *CodedAPIError) {
|
||||
i, err := minioClient.describeIdentity(ctx, identity)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsDescribeIdentityResponse{
|
||||
Policy: i.Policy,
|
||||
Admin: i.IsAdmin,
|
||||
Identity: i.Identity,
|
||||
CreatedAt: i.CreatedAt,
|
||||
CreatedBy: i.CreatedBy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetKMSDescribeSelfIdentityResponse(session *models.Principal, params kmsAPI.KMSDescribeSelfIdentityParams) (*models.KmsDescribeSelfIdentityResponse, *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 describeSelfIdentity(ctx, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func describeSelfIdentity(ctx context.Context, minioClient MinioAdmin) (*models.KmsDescribeSelfIdentityResponse, *CodedAPIError) {
|
||||
i, err := minioClient.describeSelfIdentity(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsDescribeSelfIdentityResponse{
|
||||
Policy: &models.KmsGetPolicyResponse{
|
||||
Allow: i.Policy.Allow,
|
||||
Deny: i.Policy.Deny,
|
||||
},
|
||||
Identity: i.Identity,
|
||||
Admin: i.IsAdmin,
|
||||
CreatedAt: i.CreatedAt,
|
||||
CreatedBy: i.CreatedBy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetKMSListIdentitiesResponse(session *models.Principal, params kmsAPI.KMSListIdentitiesParams) (*models.KmsListIdentitiesResponse, *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 listIdentities(ctx, pattern, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func listIdentities(ctx context.Context, pattern string, minioClient MinioAdmin) (*models.KmsListIdentitiesResponse, *CodedAPIError) {
|
||||
results, err := minioClient.listIdentities(ctx, pattern)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.KmsListIdentitiesResponse{Results: parseIdentities(results)}, nil
|
||||
}
|
||||
|
||||
func parseIdentities(results []madmin.KMSIdentityInfo) (data []*models.KmsIdentityInfo) {
|
||||
for _, policy := range results {
|
||||
data = append(data, &models.KmsIdentityInfo{
|
||||
CreatedAt: policy.CreatedAt,
|
||||
CreatedBy: policy.CreatedBy,
|
||||
Identity: policy.Identity,
|
||||
Error: policy.Error,
|
||||
Policy: policy.Policy,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func GetKMSDeleteIdentityResponse(session *models.Principal, params kmsAPI.KMSDeleteIdentityParams) *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 deleteIdentity(ctx, params.Name, AdminClient{Client: mAdmin})
|
||||
}
|
||||
|
||||
func deleteIdentity(ctx context.Context, identity string, minioClient MinioAdmin) *CodedAPIError {
|
||||
if err := minioClient.deleteIdentity(ctx, identity); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
// 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.KmsKMSImportKeyHandler)
|
||||
suite.assert.Nil(api.KmsKMSListKeysHandler)
|
||||
suite.assert.Nil(api.KmsKMSKeyStatusHandler)
|
||||
suite.assert.Nil(api.KmsKMSDeleteKeyHandler)
|
||||
suite.assert.Nil(api.KmsKMSSetPolicyHandler)
|
||||
suite.assert.Nil(api.KmsKMSAssignPolicyHandler)
|
||||
suite.assert.Nil(api.KmsKMSDescribePolicyHandler)
|
||||
suite.assert.Nil(api.KmsKMSGetPolicyHandler)
|
||||
suite.assert.Nil(api.KmsKMSListPoliciesHandler)
|
||||
suite.assert.Nil(api.KmsKMSDeletePolicyHandler)
|
||||
suite.assert.Nil(api.KmsKMSDescribeIdentityHandler)
|
||||
suite.assert.Nil(api.KmsKMSDescribeSelfIdentityHandler)
|
||||
suite.assert.Nil(api.KmsKMSListIdentitiesHandler)
|
||||
suite.assert.Nil(api.KmsKMSDeleteIdentityHandler)
|
||||
}
|
||||
|
||||
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.KmsKMSImportKeyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSListKeysHandler)
|
||||
suite.assert.NotNil(api.KmsKMSKeyStatusHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDeleteKeyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSSetPolicyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSAssignPolicyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDescribePolicyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSGetPolicyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSListPoliciesHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDeletePolicyHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDescribeIdentityHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDescribeSelfIdentityHandler)
|
||||
suite.assert.NotNil(api.KmsKMSListIdentitiesHandler)
|
||||
suite.assert.NotNil(api.KmsKMSDeleteIdentityHandler)
|
||||
}
|
||||
|
||||
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) TestKMSImportKeyHandlerWithError() {
|
||||
params, api := suite.initKMSImportKeyRequest()
|
||||
response := api.KmsKMSImportKeyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSImportKeyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSImportKeyRequest() (params kmsAPI.KMSImportKeyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSImportKeyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := importKey(ctx, "key", []byte(""), 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 (suite *KMSTestSuite) TestKMSDeleteKeyHandlerWithError() {
|
||||
params, api := suite.initKMSDeleteKeyRequest()
|
||||
response := api.KmsKMSDeleteKeyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDeleteKeyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDeleteKeyRequest() (params kmsAPI.KMSDeleteKeyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDeleteKeyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := deleteKey(ctx, "key", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSSetPolicyHandlerWithError() {
|
||||
params, api := suite.initKMSSetPolicyRequest()
|
||||
response := api.KmsKMSSetPolicyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSSetPolicyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSSetPolicyRequest() (params kmsAPI.KMSSetPolicyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
policy := "policy"
|
||||
params.Body = &models.KmsSetPolicyRequest{Policy: &policy}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSSetPolicyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := setPolicy(ctx, "policy", []byte(""), suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSAssignPolicyHandlerWithError() {
|
||||
params, api := suite.initKMSAssignPolicyRequest()
|
||||
response := api.KmsKMSAssignPolicyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSAssignPolicyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSAssignPolicyRequest() (params kmsAPI.KMSAssignPolicyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSAssignPolicyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := assignPolicy(ctx, "policy", []byte(""), suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribePolicyHandlerWithError() {
|
||||
params, api := suite.initKMSDescribePolicyRequest()
|
||||
response := api.KmsKMSDescribePolicyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDescribePolicyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDescribePolicyRequest() (params kmsAPI.KMSDescribePolicyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribePolicyWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := describePolicy(ctx, "policy", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSGetPolicyHandlerWithError() {
|
||||
params, api := suite.initKMSGetPolicyRequest()
|
||||
response := api.KmsKMSGetPolicyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSGetPolicyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSGetPolicyRequest() (params kmsAPI.KMSGetPolicyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSGetPolicyWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := getPolicy(ctx, "policy", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListPoliciesHandlerWithError() {
|
||||
params, api := suite.initKMSListPoliciesRequest()
|
||||
response := api.KmsKMSListPoliciesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSListPoliciesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSListPoliciesRequest() (params kmsAPI.KMSListPoliciesParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListPoliciesWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := listKMSPolicies(ctx, "", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDeletePolicyHandlerWithError() {
|
||||
params, api := suite.initKMSDeletePolicyRequest()
|
||||
response := api.KmsKMSDeletePolicyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDeletePolicyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDeletePolicyRequest() (params kmsAPI.KMSDeletePolicyParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDeletePolicyWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := deletePolicy(ctx, "policy", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribeIdentityHandlerWithError() {
|
||||
params, api := suite.initKMSDescribeIdentityRequest()
|
||||
response := api.KmsKMSDescribeIdentityHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDescribeIdentityDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDescribeIdentityRequest() (params kmsAPI.KMSDescribeIdentityParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribeIdentityWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := describeIdentity(ctx, "identity", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribeSelfIdentityHandlerWithError() {
|
||||
params, api := suite.initKMSDescribeSelfIdentityRequest()
|
||||
response := api.KmsKMSDescribeSelfIdentityHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDescribeSelfIdentityDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDescribeSelfIdentityRequest() (params kmsAPI.KMSDescribeSelfIdentityParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDescribeSelfIdentityWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := describeSelfIdentity(ctx, suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListIdentitiesHandlerWithError() {
|
||||
params, api := suite.initKMSListIdentitiesRequest()
|
||||
response := api.KmsKMSListIdentitiesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSListIdentitiesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSListIdentitiesRequest() (params kmsAPI.KMSListIdentitiesParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSListIdentitiesWithoutError() {
|
||||
ctx := context.Background()
|
||||
res, err := listIdentities(ctx, "", suite.adminClient)
|
||||
suite.assert.NotNil(res)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDeleteIdentityHandlerWithError() {
|
||||
params, api := suite.initKMSDeleteIdentityRequest()
|
||||
response := api.KmsKMSDeleteIdentityHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*kmsAPI.KMSDeleteIdentityDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) initKMSDeleteIdentityRequest() (params kmsAPI.KMSDeleteIdentityParams, api operations.ConsoleAPI) {
|
||||
registerKMSHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *KMSTestSuite) TestKMSDeleteIdentityWithoutError() {
|
||||
ctx := context.Background()
|
||||
err := deleteIdentity(ctx, "identity", suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
}
|
||||
|
||||
func TestKMS(t *testing.T) {
|
||||
suite.Run(t, new(KMSTestSuite))
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
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(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, *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)
|
||||
}
|
||||
var nodeList []string
|
||||
|
||||
adminResources, _ := mAdmin.ServerInfo(ctx)
|
||||
|
||||
for _, n := range adminResources.Servers {
|
||||
nodeList = append(nodeList, n.Endpoint)
|
||||
}
|
||||
|
||||
return nodeList, nil
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of notification endpoints
|
||||
api.ConfigurationNotificationEndpointListHandler = configurationApi.NotificationEndpointListHandlerFunc(func(params configurationApi.NotificationEndpointListParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getNotificationEndpointsResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewNotificationEndpointListDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return configurationApi.NewNotificationEndpointListOK().WithPayload(notifEndpoints)
|
||||
})
|
||||
// add a new notification endpoints
|
||||
api.ConfigurationAddNotificationEndpointHandler = configurationApi.AddNotificationEndpointHandlerFunc(func(params configurationApi.AddNotificationEndpointParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getAddNotificationEndpointResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewAddNotificationEndpointDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return configurationApi.NewAddNotificationEndpointCreated().WithPayload(notifEndpoints)
|
||||
})
|
||||
}
|
||||
|
||||
// getNotificationEndpoints invokes admin info and returns a list of notification endpoints
|
||||
func getNotificationEndpoints(ctx context.Context, client MinioAdmin) (*models.NotifEndpointResponse, error) {
|
||||
serverInfo, err := client.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var listEndpoints []*models.NotificationEndpointItem
|
||||
for i := range serverInfo.Services.Notifications {
|
||||
for service, endpointStatus := range serverInfo.Services.Notifications[i] {
|
||||
for j := range endpointStatus {
|
||||
for account, status := range endpointStatus[j] {
|
||||
listEndpoints = append(listEndpoints, &models.NotificationEndpointItem{
|
||||
Service: models.NofiticationService(service),
|
||||
AccountID: account,
|
||||
Status: status.Status,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build response
|
||||
return &models.NotifEndpointResponse{
|
||||
NotificationEndpoints: listEndpoints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getNotificationEndpointsResponse(session *models.Principal, params configurationApi.NotificationEndpointListParams) (*models.NotifEndpointResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
notfEndpointResp, err := getNotificationEndpoints(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
|
||||
func addNotificationEndpoint(ctx context.Context, client MinioAdmin, params *configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, error) {
|
||||
configs := []*models.ConfigurationKV{}
|
||||
var configName string
|
||||
|
||||
// we have different add validations for each service
|
||||
switch *params.Body.Service {
|
||||
case models.NofiticationServiceAmqp:
|
||||
configName = "notify_amqp"
|
||||
case models.NofiticationServiceMqtt:
|
||||
configName = "notify_mqtt"
|
||||
case models.NofiticationServiceElasticsearch:
|
||||
configName = "notify_elasticsearch"
|
||||
case models.NofiticationServiceRedis:
|
||||
configName = "notify_redis"
|
||||
case models.NofiticationServiceNats:
|
||||
configName = "notify_nats"
|
||||
case models.NofiticationServicePostgres:
|
||||
configName = "notify_postgres"
|
||||
case models.NofiticationServiceMysql:
|
||||
configName = "notify_mysql"
|
||||
case models.NofiticationServiceKafka:
|
||||
configName = "notify_kafka"
|
||||
case models.NofiticationServiceWebhook:
|
||||
configName = "notify_webhook"
|
||||
case models.NofiticationServiceNsq:
|
||||
configName = "notify_nsq"
|
||||
default:
|
||||
return nil, errors.New("provided service is not supported")
|
||||
}
|
||||
|
||||
// set all the config values if found on the param.Body.Properties
|
||||
for k, val := range params.Body.Properties {
|
||||
configs = append(configs, &models.ConfigurationKV{
|
||||
Key: k,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
|
||||
needsRestart, err := setConfigWithARNAccountID(ctx, client, &configName, configs, *params.Body.AccountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.SetNotificationEndpointResponse{
|
||||
AccountID: params.Body.AccountID,
|
||||
Properties: params.Body.Properties,
|
||||
Service: params.Body.Service,
|
||||
Restart: needsRestart,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
notfEndpointResp, err := addNotificationEndpoint(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
cfgApi "github.com/minio/console/api/operations/configuration"
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
func Test_addNotificationEndpoint(t *testing.T) {
|
||||
client := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
client MinioAdmin
|
||||
params *cfgApi.AddNotificationEndpointParams
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
mockSetConfig func(kv string) (restart bool, err error)
|
||||
want *models.SetNotificationEndpointResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid postgres",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("postgres"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("postgres"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "set config returns error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("postgres"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("error")
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid mysql",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("mysql"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("mysql"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid kafka",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"brokers": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("kafka"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"brokers": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("kafka"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid amqp",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"url": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("amqp"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"url": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("amqp"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid mqtt",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"broker": "http://localhost:8080/broker1",
|
||||
"topic": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("mqtt"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"broker": "http://localhost:8080/broker1",
|
||||
"topic": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("mqtt"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid elasticsearch",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"url": "http://localhost:8080/broker1",
|
||||
"index": "minio",
|
||||
"format": "namespace",
|
||||
},
|
||||
Service: models.NewNofiticationService("elasticsearch"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"url": "http://localhost:8080/broker1",
|
||||
"index": "minio",
|
||||
"format": "namespace",
|
||||
},
|
||||
Service: models.NewNofiticationService("elasticsearch"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid redis",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"address": "http://localhost:8080/broker1",
|
||||
"key": "minio",
|
||||
"format": "namespace",
|
||||
},
|
||||
Service: models.NewNofiticationService("redis"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"address": "http://localhost:8080/broker1",
|
||||
"key": "minio",
|
||||
"format": "namespace",
|
||||
},
|
||||
Service: models.NewNofiticationService("redis"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid nats",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"address": "http://localhost:8080/broker1",
|
||||
"subject": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("nats"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"address": "http://localhost:8080/broker1",
|
||||
"subject": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("nats"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid webhook",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"endpoint": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("webhook"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"endpoint": "http://localhost:8080/broker1",
|
||||
},
|
||||
Service: models.NewNofiticationService("webhook"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid nsq",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"nsqd_address": "http://localhost:8080/broker1",
|
||||
"topic": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("nsq"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"nsqd_address": "http://localhost:8080/broker1",
|
||||
"topic": "minio",
|
||||
},
|
||||
Service: models.NewNofiticationService("nsq"),
|
||||
Restart: false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid service",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("oorgle"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return false, errors.New("invalid config")
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid config, restart required",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
client: client,
|
||||
params: &cfgApi.AddNotificationEndpointParams{
|
||||
HTTPRequest: nil,
|
||||
Body: &models.NotificationEndpoint{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("postgres"),
|
||||
},
|
||||
},
|
||||
},
|
||||
mockSetConfig: func(_ string) (restart bool, err error) {
|
||||
return true, nil
|
||||
},
|
||||
want: &models.SetNotificationEndpointResponse{
|
||||
AccountID: swag.String("1"),
|
||||
Properties: map[string]string{
|
||||
"host": "localhost",
|
||||
"user": "user",
|
||||
"password": "passwrd",
|
||||
},
|
||||
Service: models.NewNofiticationService("postgres"),
|
||||
Restart: true,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("addNotificationEndpoint() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("addNotificationEndpoint() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
"github.com/minio/mc/cmd"
|
||||
@@ -60,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" {
|
||||
|
||||
@@ -1,720 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
bucketApi "github.com/minio/console/api/operations/bucket"
|
||||
policyApi "github.com/minio/console/api/operations/policy"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
s3 "github.com/minio/minio-go/v7"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
|
||||
policies "github.com/minio/console/api/policy"
|
||||
)
|
||||
|
||||
func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
// List Policies
|
||||
api.PolicyListPoliciesHandler = policyApi.ListPoliciesHandlerFunc(func(params policyApi.ListPoliciesParams, session *models.Principal) middleware.Responder {
|
||||
listPoliciesResponse, err := getListPoliciesResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListPoliciesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewListPoliciesOK().WithPayload(listPoliciesResponse)
|
||||
})
|
||||
// Policy Info
|
||||
api.PolicyPolicyInfoHandler = policyApi.PolicyInfoHandlerFunc(func(params policyApi.PolicyInfoParams, session *models.Principal) middleware.Responder {
|
||||
policyInfo, err := getPolicyInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewPolicyInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewPolicyInfoOK().WithPayload(policyInfo)
|
||||
})
|
||||
// Add Policy
|
||||
api.PolicyAddPolicyHandler = policyApi.AddPolicyHandlerFunc(func(params policyApi.AddPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getAddPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
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(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(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(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(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(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(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(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(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(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(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return policyApi.NewGetUserPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewGetUserPolicyOK().WithPayload(userPolicyResponse)
|
||||
})
|
||||
// Gets policies for specified user
|
||||
api.PolicyGetSAUserPolicyHandler = policyApi.GetSAUserPolicyHandlerFunc(func(params policyApi.GetSAUserPolicyParams, session *models.Principal) middleware.Responder {
|
||||
userPolicyResponse, err := getSAUserPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewGetSAUserPolicyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return policyApi.NewGetSAUserPolicyOK().WithPayload(userPolicyResponse)
|
||||
})
|
||||
}
|
||||
|
||||
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, "", getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
accessRules, _ := client.GetAccessRules(ctx)
|
||||
var accessRuleList []*models.AccessRule
|
||||
for k, v := range accessRules {
|
||||
accessRuleList = append(accessRuleList, &models.AccessRule{Prefix: k[len(bucket)+1 : len(k)-1], Access: v})
|
||||
}
|
||||
return &models.ListAccessRulesResponse{AccessRules: accessRuleList}, nil
|
||||
}
|
||||
|
||||
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, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
errorVal := client.SetAccess(ctx, prefixAccess.Access, false)
|
||||
if errorVal != nil {
|
||||
returnError := ErrorWithContext(ctx, errorVal.Cause)
|
||||
minioError := s3.ToErrorResponse(errorVal.Cause)
|
||||
if minioError.Code == "NoSuchBucket" {
|
||||
returnError.Code = 404
|
||||
}
|
||||
return false, returnError
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
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, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
errorVal := client.SetAccess(ctx, "none", false)
|
||||
if errorVal != nil {
|
||||
return false, ErrorWithContext(ctx, errorVal.Cause)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getListPoliciesWithBucketResponse(session *models.Principal, params bucketApi.ListPoliciesWithBucketParams) (*models.ListPoliciesResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
policies, err := listPoliciesWithBucket(ctx, params.Bucket, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listPoliciesResponse := &models.ListPoliciesResponse{
|
||||
Policies: policies,
|
||||
Total: int64(len(policies)),
|
||||
}
|
||||
return listPoliciesResponse, nil
|
||||
}
|
||||
|
||||
// listPoliciesWithBucket calls MinIO server to list all policy names present on the server that apply to a particular bucket.
|
||||
// listPoliciesWithBucket() converts the map[string][]byte returned by client.listPolicies()
|
||||
// to []*models.Policy by iterating over each key in policyRawMap and
|
||||
// then using Unmarshal on the raw bytes to create a *models.Policy
|
||||
func listPoliciesWithBucket(ctx context.Context, bucket string, client MinioAdmin) ([]*models.Policy, error) {
|
||||
policyMap, err := client.listPolicies(ctx)
|
||||
var policies []*models.Policy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for name, policy := range policyMap {
|
||||
policy, err := parsePolicy(name, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policyMatchesBucket(ctx, policy, bucket) {
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func policyMatchesBucket(ctx context.Context, policy *models.Policy, bucket string) bool {
|
||||
policyData := &iampolicy.Policy{}
|
||||
err := json.Unmarshal([]byte(policy.Policy), policyData)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error parsing policy: %v", err))
|
||||
return false
|
||||
}
|
||||
policyStatements := policyData.Statements
|
||||
for i := 0; i < len(policyStatements); i++ {
|
||||
resources := policyStatements[i].Resources
|
||||
if resources.Match(bucket, map[string][]string{}) {
|
||||
return true
|
||||
}
|
||||
if resources.Match(fmt.Sprintf("%s/*", bucket), map[string][]string{}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// listPolicies calls MinIO server to list all policy names present on the server.
|
||||
// listPolicies() converts the map[string][]byte returned by client.listPolicies()
|
||||
// to []*models.Policy by iterating over each key in policyRawMap and
|
||||
// then using Unmarshal on the raw bytes to create a *models.Policy
|
||||
func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, error) {
|
||||
policyMap, err := client.listPolicies(ctx)
|
||||
var policies []*models.Policy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for name, policy := range policyMap {
|
||||
policy, err := parsePolicy(name, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// getListPoliciesResponse performs listPolicies() and serializes it to the handler's output
|
||||
func getListPoliciesResponse(session *models.Principal, params policyApi.ListPoliciesParams) (*models.ListPoliciesResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listPoliciesResponse := &models.ListPoliciesResponse{
|
||||
Policies: policies,
|
||||
Total: int64(len(policies)),
|
||||
}
|
||||
return listPoliciesResponse, nil
|
||||
}
|
||||
|
||||
// getListUsersForPoliciesResponse performs lists users affected by a given policy.
|
||||
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(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
if policies[i].Name == policy {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
}
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var filteredUsers []string
|
||||
for _, user := range users {
|
||||
for _, upolicy := range user.Policy {
|
||||
if upolicy == policy {
|
||||
filteredUsers = append(filteredUsers, user.AccessKey)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(filteredUsers)
|
||||
return filteredUsers, nil
|
||||
}
|
||||
|
||||
func getUserPolicyResponse(ctx context.Context, session *models.Principal) (string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return "nil", ErrorWithContext(ctx, ErrPolicyNotFound)
|
||||
}
|
||||
tokenClaims, _ := getClaimsFromToken(session.STSSessionToken)
|
||||
|
||||
// initialize admin client
|
||||
mAdminClient, err := NewMinioAdminClient(ctx, &models.Principal{
|
||||
STSAccessKeyID: session.STSAccessKeyID,
|
||||
STSSecretAccessKey: session.STSSecretAccessKey,
|
||||
STSSessionToken: session.STSSessionToken,
|
||||
})
|
||||
if err != nil {
|
||||
return "nil", ErrorWithContext(ctx, err)
|
||||
}
|
||||
userAdminClient := AdminClient{Client: mAdminClient}
|
||||
// Obtain the current policy assigned to this user
|
||||
// necessary for generating the list of allowed endpoints
|
||||
accountInfo, err := getAccountInfo(ctx, userAdminClient)
|
||||
if err != nil {
|
||||
return "nil", ErrorWithContext(ctx, err)
|
||||
}
|
||||
rawPolicy := policies.ReplacePolicyVariables(tokenClaims, accountInfo)
|
||||
return string(rawPolicy), nil
|
||||
}
|
||||
|
||||
func getSAUserPolicyResponse(session *models.Principal, params policyApi.GetSAUserPolicyParams) (*models.AUserPolicyResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound)
|
||||
}
|
||||
// initialize admin client
|
||||
mAdminClient, err := NewMinioAdminClient(params.HTTPRequest.Context(), &models.Principal{
|
||||
STSAccessKeyID: session.STSAccessKeyID,
|
||||
STSSecretAccessKey: session.STSSecretAccessKey,
|
||||
STSSessionToken: session.STSSessionToken,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
userAdminClient := AdminClient{Client: mAdminClient}
|
||||
|
||||
userName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
user, err := getUserInfo(ctx, userAdminClient, userName)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
var userPolicies []string
|
||||
if len(user.PolicyName) > 0 {
|
||||
userPolicies = strings.Split(user.PolicyName, ",")
|
||||
}
|
||||
|
||||
for _, group := range user.MemberOf {
|
||||
groupDesc, err := groupInfo(ctx, userAdminClient, group)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
if groupDesc.Policy != "" {
|
||||
userPolicies = append(userPolicies, strings.Split(groupDesc.Policy, ",")...)
|
||||
}
|
||||
}
|
||||
|
||||
allKeys := make(map[string]bool)
|
||||
var userPolicyList []string
|
||||
|
||||
for _, item := range userPolicies {
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
userPolicyList = append(userPolicyList, item)
|
||||
}
|
||||
}
|
||||
var userStatements []iampolicy.Statement
|
||||
|
||||
for _, pol := range userPolicyList {
|
||||
policy, err := getPolicyStatements(ctx, userAdminClient, pol)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
userStatements = append(userStatements, policy...)
|
||||
}
|
||||
|
||||
combinedPolicy := iampolicy.Policy{
|
||||
Version: "2012-10-17",
|
||||
Statements: userStatements,
|
||||
}
|
||||
|
||||
stringPolicy, err := json.Marshal(combinedPolicy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
parsedPolicy := string(stringPolicy)
|
||||
|
||||
getUserPoliciesResponse := &models.AUserPolicyResponse{
|
||||
Policy: parsedPolicy,
|
||||
}
|
||||
|
||||
return getUserPoliciesResponse, nil
|
||||
}
|
||||
|
||||
func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.ListGroupsForPolicyParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a 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 {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
if policies[i].Name == policy {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
}
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var filteredGroups []string
|
||||
for _, group := range groups {
|
||||
info, err := groupInfo(ctx, adminClient, group)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
groupPolicies := strings.Split(info.Policy, ",")
|
||||
for _, groupPolicy := range groupPolicies {
|
||||
if groupPolicy == policy {
|
||||
filteredGroups = append(filteredGroups, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(filteredGroups)
|
||||
return filteredGroups, nil
|
||||
}
|
||||
|
||||
// removePolicy() calls MinIO server to remove a policy based on name.
|
||||
func removePolicy(ctx context.Context, client MinioAdmin, name string) error {
|
||||
err := client.removePolicy(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output
|
||||
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(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := removePolicy(ctx, adminClient, policyName); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addPolicy calls MinIO server to add a canned policy.
|
||||
// addPolicy() takes name and policy in string format, policy
|
||||
// policy must be string in json format, in the future this will change
|
||||
// to a Policy struct{} - https://github.com/minio/minio/issues/9171
|
||||
func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*models.Policy, error) {
|
||||
iamp, err := iampolicy.ParseConfig(bytes.NewReader([]byte(policy)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := client.addPolicy(ctx, name, iamp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policyObject, err := policyInfo(ctx, client, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policyObject, nil
|
||||
}
|
||||
|
||||
// getAddPolicyResponse performs addPolicy() and serializes it to the handler's output
|
||||
func getAddPolicyResponse(session *models.Principal, params policyApi.AddPolicyParams) (*models.Policy, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Body == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyBodyNotInRequest)
|
||||
}
|
||||
if strings.Contains(*params.Body.Name, " ") {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNameContainsSpace)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(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}
|
||||
policy, err := addPolicy(ctx, adminClient, *params.Body.Name, *params.Body.Policy)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// policyInfo calls MinIO server to retrieve information of a canned policy.
|
||||
// policyInfo() takes a policy name, obtains the []byte (represents a string in JSON format)
|
||||
// and return it as *models.Policy , in the future this will change
|
||||
// to a Policy struct{} - https://github.com/minio/minio/issues/9171
|
||||
func policyInfo(ctx context.Context, client MinioAdmin, name string) (*models.Policy, error) {
|
||||
policyRaw, err := client.getPolicy(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy, err := parsePolicy(name, policyRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// getPolicy Statements calls MinIO server to retrieve information of a canned policy.
|
||||
// and returns the associated Statements
|
||||
func getPolicyStatements(ctx context.Context, client MinioAdmin, name string) ([]iampolicy.Statement, error) {
|
||||
policyRaw, err := client.getPolicy(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policyRaw.Statements, nil
|
||||
}
|
||||
|
||||
// getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output
|
||||
func getPolicyInfoResponse(session *models.Principal, params policyApi.PolicyInfoParams) (*models.Policy, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policyName, err := utils.DecodeBase64(params.Name)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
policy, err := policyInfo(ctx, adminClient, policyName)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
// SetPolicy calls MinIO server to assign policy to a group or user.
|
||||
func SetPolicy(ctx context.Context, client MinioAdmin, name, entityName string, entityType models.PolicyEntity) error {
|
||||
isGroup := false
|
||||
if entityType == models.PolicyEntityGroup {
|
||||
isGroup = true
|
||||
}
|
||||
return client.setPolicy(ctx, name, entityName, isGroup)
|
||||
}
|
||||
|
||||
// getSetPolicyResponse() performs SetPolicy() and serializes it to the handler's output
|
||||
func getSetPolicyResponse(session *models.Principal, params policyApi.SetPolicyParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// Removing this section
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := SetPolicy(ctx, adminClient, strings.Join(params.Body.Name, ","), *params.Body.EntityName, *params.Body.EntityType); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSetPolicyMultipleResponse(session *models.Principal, params policyApi.SetPolicyMultipleParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := setPolicyMultipleEntities(ctx, adminClient, strings.Join(params.Body.Name, ","), params.Body.Users, params.Body.Groups); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setPolicyMultipleEntities sets a policy to multiple users/groups
|
||||
func setPolicyMultipleEntities(ctx context.Context, client MinioAdmin, policyName string, users, groups []models.IamEntity) error {
|
||||
for _, user := range users {
|
||||
if err := client.setPolicy(ctx, policyName, string(user), false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, group := range groups {
|
||||
groupDesc, err := groupInfo(ctx, client, string(group))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allGroupPolicies := ""
|
||||
if len(groups) > 1 {
|
||||
allGroupPolicies = groupDesc.Policy + "," + policyName
|
||||
s := strings.Split(allGroupPolicies, ",")
|
||||
allGroupPolicies = strings.Join(UniqueKeys(s), ",")
|
||||
} else {
|
||||
allGroupPolicies = policyName
|
||||
}
|
||||
if err := client.setPolicy(ctx, allGroupPolicies, string(group), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsePolicy() converts from *rawPolicy to *models.Policy
|
||||
func parsePolicy(name string, rawPolicy *iampolicy.Policy) (*models.Policy, error) {
|
||||
stringPolicy, err := json.Marshal(rawPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy := &models.Policy{
|
||||
Name: name,
|
||||
Policy: string(stringPolicy),
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
@@ -1,382 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestListPolicies(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
// mock function response from listPolicies()
|
||||
minioListPoliciesMock = func() (map[string]*iampolicy.Policy, error) {
|
||||
var readonly iampolicy.Policy
|
||||
var readwrite iampolicy.Policy
|
||||
var diagnostis iampolicy.Policy
|
||||
|
||||
for _, p := range iampolicy.DefaultPolicies {
|
||||
switch p.Name {
|
||||
case "readonly":
|
||||
readonly = p.Definition
|
||||
case "readwrite":
|
||||
readwrite = p.Definition
|
||||
case "diagnostics":
|
||||
diagnostis = p.Definition
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]*iampolicy.Policy{
|
||||
"readonly": &readonly,
|
||||
"readwrite": &readwrite,
|
||||
"diagnostics": &diagnostis,
|
||||
}, nil
|
||||
}
|
||||
// Test-1 : listPolicies() Get response from minio client with three Canned Policies and return the same number on listPolicies()
|
||||
function := "listPolicies()"
|
||||
policiesList, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// verify length of Policies is correct
|
||||
funcAssert.Equal(3, len(policiesList), fmt.Sprintf("Failed on %s: length of Policies's lists is not the same", function))
|
||||
// Test-2 : listPolicies() Return error and see that the error is handled correctly and returned
|
||||
minioListPoliciesMock = func() (map[string]*iampolicy.Policy, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
_, err = listPolicies(ctx, adminClient)
|
||||
if funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemovePolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
// Test-1 : removePolicy() remove an existing policy
|
||||
policyToRemove := "console-policy"
|
||||
minioRemovePolicyMock = func(_ string) error {
|
||||
return nil
|
||||
}
|
||||
function := "removePolicy()"
|
||||
if err := removePolicy(ctx, adminClient, policyToRemove); err != nil {
|
||||
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(_ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := removePolicy(ctx, adminClient, policyToRemove); funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(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(_ string, _ *iampolicy.Policy) error {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return iamp, nil
|
||||
}
|
||||
assertPolicy := models.Policy{
|
||||
Name: "new-policy",
|
||||
Policy: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetBucketLocation\",\"s3:GetObject\",\"s3:ListAllMyBuckets\"],\"Resource\":[\"arn:aws:s3:::*\"]}]}",
|
||||
}
|
||||
// Test-1 : addPolicy() adds a new policy
|
||||
function := "addPolicy()"
|
||||
policy, err := addPolicy(ctx, adminClient, policyName, policyDefinition)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
} else {
|
||||
funcAssert.Equal(policy.Name, assertPolicy.Name)
|
||||
|
||||
var expectedPolicy iampolicy.Policy
|
||||
var actualPolicy iampolicy.Policy
|
||||
err1 := json.Unmarshal([]byte(policy.Policy), &expectedPolicy)
|
||||
funcAssert.NoError(err1)
|
||||
err2 := json.Unmarshal([]byte(assertPolicy.Policy), &actualPolicy)
|
||||
funcAssert.NoError(err2)
|
||||
funcAssert.Equal(expectedPolicy, actualPolicy)
|
||||
}
|
||||
// Test-2 : addPolicy() got an error while adding policy
|
||||
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(_ string, _ *iampolicy.Policy) error {
|
||||
return nil
|
||||
}
|
||||
minioGetPolicyMock = func(_ string) (*iampolicy.Policy, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPolicy(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
funcAssert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
policyName := "readOnly"
|
||||
entityName := "alevsk"
|
||||
entityObject := models.PolicyEntityUser
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
}
|
||||
// Test-1 : SetPolicy() set policy to user
|
||||
function := "SetPolicy()"
|
||||
err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-2 : SetPolicy() set policy to group
|
||||
entityObject = models.PolicyEntityGroup
|
||||
err = SetPolicy(ctx, adminClient, policyName, entityName, entityObject)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-3 : SetPolicy() set policy to user and get error
|
||||
entityObject = models.PolicyEntityUser
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
// Test-4 : SetPolicy() set policy to group and get error
|
||||
entityObject = models.PolicyEntityGroup
|
||||
minioSetPolicyMock = func(_, _ string, _ bool) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := SetPolicy(ctx, adminClient, policyName, entityName, entityObject); funcAssert.Error(err) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SetPolicyMultiple(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
type args struct {
|
||||
policyName string
|
||||
users []models.IamEntity
|
||||
groups []models.IamEntity
|
||||
setPolicyFunc func(policyName, entityName string, isGroup bool) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
errorExpected error
|
||||
}{
|
||||
{
|
||||
name: "Set policy to multiple users and groups",
|
||||
args: args{
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{"user1", "user2"},
|
||||
groups: []models.IamEntity{"group1", "group2"},
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
errorExpected: nil,
|
||||
},
|
||||
{
|
||||
name: "Return error on set policy function",
|
||||
args: args{
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{"user1", "user2"},
|
||||
groups: []models.IamEntity{"group1", "group2"},
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return errors.New("error set")
|
||||
},
|
||||
},
|
||||
errorExpected: errors.New("error set"),
|
||||
},
|
||||
{
|
||||
// Description: Empty lists of users and groups are acceptable
|
||||
name: "Empty lists of users and groups",
|
||||
args: args{
|
||||
policyName: "readonly",
|
||||
users: []models.IamEntity{},
|
||||
groups: []models.IamEntity{},
|
||||
setPolicyFunc: func(_, _ string, _ bool) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
errorExpected: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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) {
|
||||
ji, _ := json.Marshal(got)
|
||||
vi, _ := json.Marshal(tt.errorExpected)
|
||||
t.Errorf("got %s want %s", ji, vi)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_policyMatchesBucket(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *models.Policy
|
||||
bucket string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Test1",
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"admin:*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`}, bucket: "test1"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Test2",
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`}, bucket: "test1"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Test3",
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "VisualEditor0",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:ListStorageLensConfigurations",
|
||||
"s3:GetAccessPoint",
|
||||
"s3:PutAccountPublicAccessBlock",
|
||||
"s3:GetAccountPublicAccessBlock",
|
||||
"s3:ListAllMyBuckets",
|
||||
"s3:ListAccessPoints",
|
||||
"s3:ListJobs",
|
||||
"s3:PutStorageLensConfiguration",
|
||||
"s3:CreateJob"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "VisualEditor1",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:*",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::test",
|
||||
"arn:aws:s3:::test/*",
|
||||
"arn:aws:s3:::lkasdkljasd090901",
|
||||
"arn:aws:s3:::lkasdkljasd090901/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`}, bucket: "test1"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Test4",
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`}, bucket: "bucket1"},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
var items []*models.StartProfilingItem
|
||||
|
||||
type profileOptions struct {
|
||||
Types string
|
||||
}
|
||||
|
||||
func getProfileOptionsFromReq(req *http.Request) (*profileOptions, error) {
|
||||
pOptions := profileOptions{}
|
||||
pOptions.Types = req.FormValue("types")
|
||||
return &pOptions, nil
|
||||
}
|
||||
|
||||
func startProfiling(ctx context.Context, conn WSConn, client MinioAdmin, pOpts *profileOptions) error {
|
||||
profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(pOpts.Types))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items = []*models.StartProfilingItem{}
|
||||
for _, result := range profilingResults {
|
||||
items = append(items, &models.StartProfilingItem{
|
||||
Success: result.Success,
|
||||
Error: result.Error,
|
||||
NodeName: result.NodeName,
|
||||
})
|
||||
}
|
||||
zippedData, err := client.stopProfiling(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message, err := io.ReadAll(zippedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.writeMessage(websocket.BinaryMessage, message)
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Implementing fake closingBuffer to mock stopProfiling() (io.ReadCloser, error)
|
||||
type ClosingBuffer struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
// Implementing a fake Close function for io.ReadCloser
|
||||
func (cb *ClosingBuffer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestStartProfiling(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startProfiling()"
|
||||
testOptions := &profileOptions{
|
||||
Types: "cpu",
|
||||
}
|
||||
|
||||
// Test-1 : startProfiling() Get response from MinIO server with one profiling object without errors
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(_ madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return []madmin.StartProfilingResult{
|
||||
{
|
||||
NodeName: "http://127.0.0.1:9000/",
|
||||
Success: true,
|
||||
Error: "",
|
||||
},
|
||||
{
|
||||
NodeName: "http://127.0.0.1:9001/",
|
||||
Success: true,
|
||||
Error: "",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// mock function response from stopProfiling()
|
||||
minioStopProfiling = func() (io.ReadCloser, error) {
|
||||
return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil
|
||||
}
|
||||
// mock function response from mockConn.writeMessage()
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
err := startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(err, nil)
|
||||
|
||||
// Test-2 : startProfiling() Correctly handles errors returned by MinIO
|
||||
// mock function response from startProfiling()
|
||||
minioStartProfiling = func(_ madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
err = startProfiling(ctx, mockWSConn, adminClient, testOptions)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: getProfileOptionsFromReq() correctly returns profile options from request
|
||||
u, _ := url.Parse("ws://localhost/ws/profile?types=cpu,mem,block,mutex,trace,threads,goroutines")
|
||||
req := &http.Request{
|
||||
URL: u,
|
||||
}
|
||||
opts, err := getProfileOptionsFromReq(req)
|
||||
if assert.NoError(err) {
|
||||
expectedOptions := profileOptions{
|
||||
Types: "cpu,mem,block,mutex,trace,threads,goroutines",
|
||||
}
|
||||
assert.Equal(expectedOptions.Types, opts.Types)
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
release "github.com/minio/console/api/operations/release"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/pkg/v2/env"
|
||||
)
|
||||
|
||||
var (
|
||||
releaseServiceHostEnvVar = "RELEASE_SERVICE_HOST"
|
||||
defaultReleaseServiceHost = "https://enterprise-updates.ic.min.dev"
|
||||
)
|
||||
|
||||
func registerReleasesHandlers(api *operations.ConsoleAPI) {
|
||||
api.ReleaseListReleasesHandler = release.ListReleasesHandlerFunc(func(params release.ListReleasesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetReleaseListResponse(session, params)
|
||||
if err != nil {
|
||||
return release.NewListReleasesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return release.NewListReleasesOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetReleaseListResponse(_ *models.Principal, params release.ListReleasesParams) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
repo := params.Repo
|
||||
currentRelease := ""
|
||||
if params.Current != nil {
|
||||
currentRelease = *params.Current
|
||||
}
|
||||
search := ""
|
||||
if params.Search != nil {
|
||||
search = *params.Search
|
||||
}
|
||||
filter := ""
|
||||
if params.Filter != nil {
|
||||
filter = *params.Filter
|
||||
}
|
||||
ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest))
|
||||
return releaseList(ctx, repo, currentRelease, search, filter)
|
||||
}
|
||||
|
||||
func releaseList(ctx context.Context, repo, currentRelease, search, filter string) (*models.ReleaseListResponse, *CodedAPIError) {
|
||||
serviceURL := getReleaseServiceURL()
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
releases, err := getReleases(serviceURL, repo, currentRelease, search, filter, clientIP)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func getReleaseServiceURL() string {
|
||||
host := env.Get(releaseServiceHostEnvVar, defaultReleaseServiceHost)
|
||||
return fmt.Sprintf("%s/releases", host)
|
||||
}
|
||||
|
||||
func getReleases(endpoint, repo, currentRelease, search, filter, clientIP string) (*models.ReleaseListResponse, error) {
|
||||
rl := &models.ReleaseListResponse{}
|
||||
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := &url.Values{}
|
||||
q.Add("repo", repo)
|
||||
q.Add("search", search)
|
||||
q.Add("filter", filter)
|
||||
q.Add("current", currentRelease)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := GetConsoleHTTPClient(clientIP)
|
||||
client.Timeout = time.Second * 5
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("error getting releases: %s", resp.Status)
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&rl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rl, nil
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
release "github.com/minio/console/api/operations/release"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ReleasesTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
getServer *httptest.Server
|
||||
withError bool
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.getServer = httptest.NewServer(http.HandlerFunc(suite.getHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(releaseServiceHostEnvVar)
|
||||
os.Setenv(releaseServiceHostEnvVar, suite.getServer.URL)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TearDownSuite() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(releaseServiceHostEnvVar, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(releaseServiceHostEnvVar)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) getHandler(
|
||||
w http.ResponseWriter, _ *http.Request,
|
||||
) {
|
||||
if suite.withError {
|
||||
w.WriteHeader(400)
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
response := &models.ReleaseListResponse{}
|
||||
bytes, _ := json.Marshal(response)
|
||||
fmt.Fprint(w, string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestRegisterReleasesHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assert.Nil(api.ReleaseListReleasesHandler)
|
||||
registerReleasesHandlers(api)
|
||||
suite.assert.NotNil(api.ReleaseListReleasesHandler)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestGetReleasesWithError() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
current := "mock"
|
||||
registerReleasesHandlers(api)
|
||||
params := release.NewListReleasesParams()
|
||||
params.Current = ¤t
|
||||
params.HTTPRequest = &http.Request{}
|
||||
suite.withError = true
|
||||
response := api.ReleaseListReleasesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*release.ListReleasesDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *ReleasesTestSuite) TestGetReleasesWithoutError() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
registerReleasesHandlers(api)
|
||||
params := release.NewListReleasesParams()
|
||||
params.HTTPRequest = &http.Request{}
|
||||
suite.withError = false
|
||||
response := api.ReleaseListReleasesHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*release.ListReleasesOK)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func TestReleases(t *testing.T) {
|
||||
suite.Run(t, new(ReleasesTestSuite))
|
||||
}
|
||||
@@ -1,810 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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/minio-go/v7/pkg/replication"
|
||||
)
|
||||
|
||||
type RemoteBucketResult struct {
|
||||
OriginBucket string
|
||||
TargetBucket string
|
||||
Error string
|
||||
}
|
||||
|
||||
func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
// return list of remote buckets
|
||||
api.BucketListRemoteBucketsHandler = bucketApi.ListRemoteBucketsHandlerFunc(func(params bucketApi.ListRemoteBucketsParams, session *models.Principal) middleware.Responder {
|
||||
listResp, err := getListRemoteBucketsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListRemoteBucketsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListRemoteBucketsOK().WithPayload(listResp)
|
||||
})
|
||||
|
||||
// return information about a specific bucket
|
||||
api.BucketRemoteBucketDetailsHandler = bucketApi.RemoteBucketDetailsHandlerFunc(func(params bucketApi.RemoteBucketDetailsParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getRemoteBucketDetailsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewRemoteBucketDetailsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewRemoteBucketDetailsOK().WithPayload(response)
|
||||
})
|
||||
|
||||
// delete remote bucket
|
||||
api.BucketDeleteRemoteBucketHandler = bucketApi.DeleteRemoteBucketHandlerFunc(func(params bucketApi.DeleteRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getDeleteRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteRemoteBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewDeleteRemoteBucketNoContent()
|
||||
})
|
||||
|
||||
// set remote bucket
|
||||
api.BucketAddRemoteBucketHandler = bucketApi.AddRemoteBucketHandlerFunc(func(params bucketApi.AddRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewAddRemoteBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewAddRemoteBucketCreated()
|
||||
})
|
||||
|
||||
// set multi-bucket replication
|
||||
api.BucketSetMultiBucketReplicationHandler = bucketApi.SetMultiBucketReplicationHandlerFunc(func(params bucketApi.SetMultiBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
response, err := setMultiBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewSetMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewSetMultiBucketReplicationOK().WithPayload(response)
|
||||
})
|
||||
|
||||
// list external buckets
|
||||
api.BucketListExternalBucketsHandler = bucketApi.ListExternalBucketsHandlerFunc(func(params bucketApi.ListExternalBucketsParams, _ *models.Principal) middleware.Responder {
|
||||
response, err := listExternalBucketsResponse(params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListExternalBucketsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewListExternalBucketsOK().WithPayload(response)
|
||||
})
|
||||
|
||||
// delete replication rule
|
||||
api.BucketDeleteBucketReplicationRuleHandler = bucketApi.DeleteBucketReplicationRuleHandlerFunc(func(params bucketApi.DeleteBucketReplicationRuleParams, session *models.Principal) middleware.Responder {
|
||||
err := deleteReplicationRuleResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteBucketReplicationRuleDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteBucketReplicationRuleNoContent()
|
||||
})
|
||||
|
||||
// delete all replication rules for a bucket
|
||||
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.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(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewDeleteAllReplicationRulesNoContent()
|
||||
})
|
||||
|
||||
// delete selected replication rules for a bucket
|
||||
api.BucketDeleteSelectedReplicationRulesHandler = bucketApi.DeleteSelectedReplicationRulesHandlerFunc(func(params bucketApi.DeleteSelectedReplicationRulesParams, session *models.Principal) middleware.Responder {
|
||||
err := deleteSelectedReplicationRulesResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesNoContent()
|
||||
})
|
||||
|
||||
// update local bucket replication config item
|
||||
api.BucketUpdateMultiBucketReplicationHandler = bucketApi.UpdateMultiBucketReplicationHandlerFunc(func(params bucketApi.UpdateMultiBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
err := updateBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewUpdateMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewUpdateMultiBucketReplicationCreated()
|
||||
})
|
||||
}
|
||||
|
||||
func getListRemoteBucketsResponse(session *models.Principal, params bucketApi.ListRemoteBucketsParams) (*models.ListRemoteBucketsResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
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}
|
||||
return listRemoteBuckets(ctx, adminClient)
|
||||
}
|
||||
|
||||
func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
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}
|
||||
return getRemoteBucket(ctx, adminClient, params.Name)
|
||||
}
|
||||
|
||||
func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
err = deleteRemoteBucket(ctx, adminClient, params.SourceBucketName, params.Arn)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error deleting remote bucket: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
_, err = addRemoteBucket(ctx, adminClient, *params.Body)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error adding remote bucket: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listRemoteBuckets(ctx context.Context, client MinioAdmin) (*models.ListRemoteBucketsResponse, *CodedAPIError) {
|
||||
var remoteBuckets []*models.RemoteBucket
|
||||
buckets, err := client.listRemoteBuckets(ctx, "", "")
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error listing remote buckets: %v", err))
|
||||
}
|
||||
for _, bucket := range buckets {
|
||||
remoteBucket := &models.RemoteBucket{
|
||||
AccessKey: swag.String(bucket.Credentials.AccessKey),
|
||||
RemoteARN: swag.String(bucket.Arn),
|
||||
SecretKey: bucket.Credentials.SecretKey,
|
||||
Service: "replication",
|
||||
SourceBucket: swag.String(bucket.SourceBucket),
|
||||
Status: "",
|
||||
TargetBucket: bucket.TargetBucket,
|
||||
TargetURL: bucket.Endpoint,
|
||||
SyncMode: "async",
|
||||
Bandwidth: bucket.BandwidthLimit,
|
||||
HealthCheckPeriod: int64(bucket.HealthCheckDuration.Seconds()),
|
||||
}
|
||||
if bucket.ReplicationSync {
|
||||
remoteBucket.SyncMode = "sync"
|
||||
}
|
||||
remoteBuckets = append(remoteBuckets, remoteBucket)
|
||||
}
|
||||
|
||||
return &models.ListRemoteBucketsResponse{
|
||||
Buckets: remoteBuckets,
|
||||
Total: int64(len(remoteBuckets)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getRemoteBucket(ctx context.Context, client MinioAdmin, name string) (*models.RemoteBucket, *CodedAPIError) {
|
||||
remoteBucket, err := client.getRemoteBucket(ctx, name, "")
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error getting remote bucket details: %v", err))
|
||||
}
|
||||
if remoteBucket == nil {
|
||||
return nil, ErrorWithContext(ctx, "error getting remote bucket details: bucket not found")
|
||||
}
|
||||
return &models.RemoteBucket{
|
||||
AccessKey: &remoteBucket.Credentials.AccessKey,
|
||||
RemoteARN: &remoteBucket.Arn,
|
||||
SecretKey: remoteBucket.Credentials.SecretKey,
|
||||
Service: "replication",
|
||||
SourceBucket: &remoteBucket.SourceBucket,
|
||||
Status: "",
|
||||
TargetBucket: remoteBucket.TargetBucket,
|
||||
TargetURL: remoteBucket.Endpoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deleteRemoteBucket(ctx context.Context, client MinioAdmin, sourceBucketName, arn string) error {
|
||||
return client.removeRemoteBucket(ctx, sourceBucketName, arn)
|
||||
}
|
||||
|
||||
func addRemoteBucket(ctx context.Context, client MinioAdmin, params models.CreateRemoteBucket) (string, error) {
|
||||
TargetURL := *params.TargetURL
|
||||
accessKey := *params.AccessKey
|
||||
secretKey := *params.SecretKey
|
||||
u, err := url.Parse(TargetURL)
|
||||
if err != nil {
|
||||
return "", errors.New("malformed Remote target URL")
|
||||
}
|
||||
secure := u.Scheme == "https"
|
||||
host := u.Host
|
||||
if u.Port() == "" {
|
||||
port := 80
|
||||
if secure {
|
||||
port = 443
|
||||
}
|
||||
host = host + ":" + strconv.Itoa(port)
|
||||
}
|
||||
creds := &madmin.Credentials{AccessKey: accessKey, SecretKey: secretKey}
|
||||
remoteBucket := &madmin.BucketTarget{
|
||||
TargetBucket: *params.TargetBucket,
|
||||
Secure: secure,
|
||||
Credentials: creds,
|
||||
Endpoint: host,
|
||||
Path: "",
|
||||
API: "s3v4",
|
||||
Type: "replication",
|
||||
Region: params.Region,
|
||||
ReplicationSync: *params.SyncMode == "sync",
|
||||
}
|
||||
if *params.SyncMode == "async" {
|
||||
remoteBucket.BandwidthLimit = params.Bandwidth
|
||||
}
|
||||
if params.HealthCheckPeriod > 0 {
|
||||
remoteBucket.HealthCheckDuration = time.Duration(params.HealthCheckPeriod) * time.Second
|
||||
}
|
||||
bucketARN, err := client.addRemoteBucket(ctx, *params.SourceBucket, remoteBucket)
|
||||
|
||||
return bucketARN, err
|
||||
}
|
||||
|
||||
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 {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err))
|
||||
}
|
||||
|
||||
// add rule
|
||||
maxPrio := 0
|
||||
|
||||
if priority <= 0 { // We pick next priority by default
|
||||
for _, r := range cfg.Rules {
|
||||
if r.Priority > maxPrio {
|
||||
maxPrio = r.Priority
|
||||
}
|
||||
}
|
||||
maxPrio++
|
||||
} else { // User picked priority, we try to set this manually
|
||||
maxPrio = int(priority)
|
||||
}
|
||||
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
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
|
||||
repDelMarkStatus := "disable"
|
||||
if repDelMark {
|
||||
repDelMarkStatus = "enable"
|
||||
}
|
||||
|
||||
repDelsStatus := "disable"
|
||||
if repDels {
|
||||
repDelsStatus = "enable"
|
||||
}
|
||||
|
||||
repMetaStatus := "disable"
|
||||
if repMeta {
|
||||
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: existingRepStatus,
|
||||
ReplicateDeleteMarkers: repDelMarkStatus,
|
||||
ReplicateDeletes: repDelsStatus,
|
||||
ReplicaSync: repMetaStatus,
|
||||
StorageClass: storageClass,
|
||||
}
|
||||
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error creating replication for bucket: %v", err2.Cause))
|
||||
return err2.Cause
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func editBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, ruleID, bucketName, prefix, destinationARN string, ruleStatus, repDelMark, repDels, repMeta, existingObjectRep bool, tags string, priority int32, storageClass string) error {
|
||||
// we will tolerate this call failing
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err))
|
||||
}
|
||||
|
||||
maxPrio := int(priority)
|
||||
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix, 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}
|
||||
|
||||
ruleState := "disable"
|
||||
if ruleStatus {
|
||||
ruleState = "enable"
|
||||
}
|
||||
|
||||
repDelMarkStatus := "disable"
|
||||
if repDelMark {
|
||||
repDelMarkStatus = "enable"
|
||||
}
|
||||
|
||||
repDelsStatus := "disable"
|
||||
if repDels {
|
||||
repDelsStatus = "enable"
|
||||
}
|
||||
|
||||
repMetaStatus := "disable"
|
||||
if repMeta {
|
||||
repMetaStatus = "enable"
|
||||
}
|
||||
|
||||
existingRepStatus := "disable"
|
||||
if existingObjectRep {
|
||||
existingRepStatus = "enable"
|
||||
}
|
||||
|
||||
opts := replication.Options{
|
||||
ID: ruleID,
|
||||
Priority: fmt.Sprintf("%d", maxPrio),
|
||||
RuleStatus: ruleState,
|
||||
DestBucket: destinationARN,
|
||||
Op: replication.SetOption,
|
||||
TagString: tags,
|
||||
IsTagSet: true,
|
||||
ExistingObjectReplicate: existingRepStatus,
|
||||
ReplicateDeleteMarkers: repDelMarkStatus,
|
||||
ReplicateDeletes: repDelsStatus,
|
||||
ReplicaSync: repMetaStatus,
|
||||
StorageClass: storageClass,
|
||||
IsSCSet: true,
|
||||
}
|
||||
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("error modifying replication for bucket: %v", err2.Cause)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setMultiBucketReplication(ctx context.Context, session *models.Principal, client MinioAdmin, minClient minioClient, params bucketApi.SetMultiBucketReplicationParams) []RemoteBucketResult {
|
||||
bucketsRelation := params.Body.BucketsRelation
|
||||
|
||||
// Parallel remote bucket adding
|
||||
parallelRemoteBucket := func(bucketRelationData *models.MultiBucketsRelation) chan RemoteBucketResult {
|
||||
remoteProc := make(chan RemoteBucketResult)
|
||||
sourceBucket := bucketRelationData.OriginBucket
|
||||
targetBucket := bucketRelationData.DestinationBucket
|
||||
|
||||
go func() {
|
||||
defer close(remoteProc)
|
||||
|
||||
createRemoteBucketParams := models.CreateRemoteBucket{
|
||||
AccessKey: params.Body.AccessKey,
|
||||
SecretKey: params.Body.SecretKey,
|
||||
SourceBucket: &sourceBucket,
|
||||
TargetBucket: &targetBucket,
|
||||
Region: params.Body.Region,
|
||||
TargetURL: params.Body.TargetURL,
|
||||
SyncMode: params.Body.SyncMode,
|
||||
Bandwidth: params.Body.Bandwidth,
|
||||
HealthCheckPeriod: params.Body.HealthCheckPeriod,
|
||||
}
|
||||
|
||||
// We add the remote bucket reference & store the arn or errors returned
|
||||
arn, err := addRemoteBucket(ctx, client, createRemoteBucketParams)
|
||||
|
||||
if err == nil {
|
||||
err = addBucketReplicationItem(
|
||||
ctx,
|
||||
session,
|
||||
minClient,
|
||||
sourceBucket,
|
||||
params.Body.Prefix,
|
||||
arn,
|
||||
params.Body.ReplicateExistingObjects,
|
||||
params.Body.ReplicateDeleteMarkers,
|
||||
params.Body.ReplicateDeletes,
|
||||
params.Body.ReplicateMetadata,
|
||||
params.Body.Tags,
|
||||
params.Body.Priority,
|
||||
params.Body.StorageClass)
|
||||
}
|
||||
|
||||
errorReturn := ""
|
||||
|
||||
if err != nil {
|
||||
deleteRemoteBucket(ctx, client, sourceBucket, arn)
|
||||
errorReturn = err.Error()
|
||||
}
|
||||
|
||||
retParams := RemoteBucketResult{
|
||||
OriginBucket: sourceBucket,
|
||||
TargetBucket: targetBucket,
|
||||
Error: errorReturn,
|
||||
}
|
||||
|
||||
remoteProc <- retParams
|
||||
}()
|
||||
return remoteProc
|
||||
}
|
||||
|
||||
var bucketsManagement []chan RemoteBucketResult
|
||||
|
||||
for _, bucketName := range bucketsRelation {
|
||||
// We generate the ARNs for each bucket
|
||||
rBucket := parallelRemoteBucket(bucketName)
|
||||
bucketsManagement = append(bucketsManagement, rBucket)
|
||||
}
|
||||
|
||||
resultsList := []RemoteBucketResult{}
|
||||
for _, result := range bucketsManagement {
|
||||
res := <-result
|
||||
resultsList = append(resultsList, res)
|
||||
}
|
||||
|
||||
return resultsList
|
||||
}
|
||||
|
||||
func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
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, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err))
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
mnClient := minioClient{client: mClient}
|
||||
|
||||
replicationResults := setMultiBucketReplication(ctx, session, adminClient, mnClient, params)
|
||||
|
||||
if replicationResults == nil {
|
||||
return nil, ErrorWithContext(ctx, errors.New("error setting buckets replication"))
|
||||
}
|
||||
|
||||
resParsed := []*models.MultiBucketResponseItem{}
|
||||
|
||||
for _, repResult := range replicationResults {
|
||||
responseItem := models.MultiBucketResponseItem{
|
||||
ErrorString: repResult.Error,
|
||||
OriginBucket: repResult.OriginBucket,
|
||||
TargetBucket: repResult.TargetBucket,
|
||||
}
|
||||
|
||||
resParsed = append(resParsed, &responseItem)
|
||||
}
|
||||
|
||||
resultsParsed := models.MultiBucketResponseState{
|
||||
ReplicationState: resParsed,
|
||||
}
|
||||
|
||||
return &resultsParsed, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
return &models.ListBucketsResponse{
|
||||
Buckets: buckets,
|
||||
Total: int64(len(buckets)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getARNFromID(conf *replication.Config, rule string) string {
|
||||
for i := range conf.Rules {
|
||||
if conf.Rules[i].ID == rule {
|
||||
return conf.Rules[i].Destination.Bucket
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getARNsFromIDs(conf *replication.Config, rules []string) []string {
|
||||
temp := make(map[string]string)
|
||||
for i := range conf.Rules {
|
||||
temp[conf.Rules[i].ID] = conf.Rules[i].Destination.Bucket
|
||||
}
|
||||
var retval []string
|
||||
for i := range rules {
|
||||
if val, ok := temp[rules[i]]; ok {
|
||||
retval = append(retval, val)
|
||||
}
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
func deleteReplicationRule(ctx context.Context, session *models.Principal, bucketName, ruleID string) error {
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
mClient, err := newMinioClient(session, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minClient := minioClient{client: mClient}
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "", clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
|
||||
opts := replication.Options{
|
||||
ID: ruleID,
|
||||
Op: replication.RemoveOption,
|
||||
}
|
||||
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
return err2.Cause
|
||||
}
|
||||
|
||||
// Replication rule was successfully deleted. We remove remote bucket
|
||||
err3 := deleteRemoteBucket(ctx, admClient, bucketName, getARNFromID(&cfg, ruleID))
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAllReplicationRules(ctx context.Context, session *models.Principal, bucketName string) error {
|
||||
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, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minClient := minioClient{client: mClient}
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
err2 := mcClient.deleteAllReplicationRules(ctx)
|
||||
|
||||
if err2 != nil {
|
||||
return err2.ToGoError()
|
||||
}
|
||||
|
||||
for i := range cfg.Rules {
|
||||
err3 := deleteRemoteBucket(ctx, admClient, bucketName, cfg.Rules[i].Destination.Bucket)
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSelectedReplicationRules(ctx context.Context, session *models.Principal, bucketName string, rules []string) error {
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
mClient, err := newMinioClient(session, clientIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minClient := minioClient{client: mClient}
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(ctx, session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ARNs := getARNsFromIDs(&cfg, rules)
|
||||
|
||||
for i := range rules {
|
||||
opts := replication.Options{
|
||||
ID: rules[i],
|
||||
Op: replication.RemoveOption,
|
||||
}
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
return err2.Cause
|
||||
}
|
||||
|
||||
// In case replication rule was deleted successfully, we remove the remote bucket ARN
|
||||
err3 := deleteRemoteBucket(ctx, admClient, bucketName, ARNs[i])
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest))
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minClient := minioClient{client: mClient}
|
||||
|
||||
err = editBucketReplicationItem(
|
||||
ctx,
|
||||
session,
|
||||
minClient,
|
||||
params.RuleID,
|
||||
params.BucketName,
|
||||
params.Body.Prefix,
|
||||
params.Body.Arn,
|
||||
params.Body.RuleState,
|
||||
params.Body.ReplicateDeleteMarkers,
|
||||
params.Body.ReplicateDeletes,
|
||||
params.Body.ReplicateMetadata,
|
||||
params.Body.ReplicateExistingObjects,
|
||||
params.Body.Tags,
|
||||
params.Body.Priority,
|
||||
params.Body.StorageClass)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
siteRepApi "github.com/minio/console/api/operations/site_replication"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func registerSiteReplicationStatusHandler(api *operations.ConsoleAPI) {
|
||||
api.SiteReplicationGetSiteReplicationStatusHandler = siteRepApi.GetSiteReplicationStatusHandlerFunc(func(params siteRepApi.GetSiteReplicationStatusParams, session *models.Principal) middleware.Responder {
|
||||
rInfo, err := getSRStatusResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewGetSiteReplicationStatusDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return siteRepApi.NewGetSiteReplicationStatusOK().WithPayload(rInfo)
|
||||
})
|
||||
}
|
||||
|
||||
func getSRStatusResponse(session *models.Principal, params siteRepApi.GetSiteReplicationStatusParams) (*models.SiteReplicationStatusResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
res, err := getSRStats(ctx, adminClient, params)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getSRStats(ctx context.Context, client MinioAdmin, params siteRepApi.GetSiteReplicationStatusParams) (info *models.SiteReplicationStatusResponse, err error) {
|
||||
srParams := madmin.SRStatusOptions{
|
||||
Buckets: *params.Buckets,
|
||||
Policies: *params.Policies,
|
||||
Users: *params.Users,
|
||||
Groups: *params.Groups,
|
||||
}
|
||||
if params.EntityType != nil && params.EntityValue != nil {
|
||||
srParams.Entity = madmin.GetSREntityType(*params.EntityType)
|
||||
srParams.EntityValue = *params.EntityValue
|
||||
}
|
||||
|
||||
srInfo, err := client.getSiteReplicationStatus(ctx, srParams)
|
||||
|
||||
retInfo := models.SiteReplicationStatusResponse{
|
||||
BucketStats: &srInfo.BucketStats,
|
||||
Enabled: srInfo.Enabled,
|
||||
GroupStats: srInfo.GroupStats,
|
||||
MaxBuckets: int64(srInfo.MaxBuckets),
|
||||
MaxGroups: int64(srInfo.MaxGroups),
|
||||
MaxPolicies: int64(srInfo.MaxPolicies),
|
||||
MaxUsers: int64(srInfo.MaxUsers),
|
||||
PolicyStats: &srInfo.PolicyStats,
|
||||
Sites: &srInfo.Sites,
|
||||
StatsSummary: srInfo.StatsSummary,
|
||||
UserStats: &srInfo.UserStats,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &retInfo, nil
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
"github.com/minio/console/models"
|
||||
|
||||
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(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return svcApi.NewRestartServiceNoContent()
|
||||
})
|
||||
}
|
||||
|
||||
// serviceRestart - restarts the MinIO cluster
|
||||
func serviceRestart(ctx context.Context, client MinioAdmin) error {
|
||||
if err := client.serviceRestart(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
// copy behavior from minio/mc mainAdminServiceRestart()
|
||||
//
|
||||
// Max. time taken by the server to shutdown is 5 seconds.
|
||||
// This can happen when there are lot of s3 requests pending when the server
|
||||
// receives a restart command.
|
||||
// Sleep for 6 seconds and then check if the server is online.
|
||||
time.Sleep(6 * time.Second)
|
||||
|
||||
// Fetch the service status of the specified MinIO server
|
||||
_, err := client.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRestartServiceResponse performs serviceRestart()
|
||||
func getRestartServiceResponse(session *models.Principal, params svcApi.RestartServiceParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServiceRestart(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx := context.Background()
|
||||
function := "serviceRestart()"
|
||||
// Test-1 : serviceRestart() restart services no errors
|
||||
// mock function response from listGroups()
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : serviceRestart() returns errors on client.serviceRestart call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3 : serviceRestart() returns errors on client.serverInfo() call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{}, errors.New("error on server info")
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); assert.Error(err) {
|
||||
assert.Equal("error on server info", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
siteRepApi "github.com/minio/console/api/operations/site_replication"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func registerSiteReplicationHandler(api *operations.ConsoleAPI) {
|
||||
api.SiteReplicationGetSiteReplicationInfoHandler = siteRepApi.GetSiteReplicationInfoHandlerFunc(func(params siteRepApi.GetSiteReplicationInfoParams, session *models.Principal) middleware.Responder {
|
||||
rInfo, err := getSRInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewGetSiteReplicationInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return siteRepApi.NewGetSiteReplicationInfoOK().WithPayload(rInfo)
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationInfoAddHandler = siteRepApi.SiteReplicationInfoAddHandlerFunc(func(params siteRepApi.SiteReplicationInfoAddParams, session *models.Principal) middleware.Responder {
|
||||
eInfo, err := getSRAddResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationInfoAddDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return siteRepApi.NewSiteReplicationInfoAddOK().WithPayload(eInfo)
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationRemoveHandler = siteRepApi.SiteReplicationRemoveHandlerFunc(func(params siteRepApi.SiteReplicationRemoveParams, session *models.Principal) middleware.Responder {
|
||||
remRes, err := getSRRemoveResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return siteRepApi.NewSiteReplicationRemoveNoContent().WithPayload(remRes)
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationEditHandler = siteRepApi.SiteReplicationEditHandlerFunc(func(params siteRepApi.SiteReplicationEditParams, session *models.Principal) middleware.Responder {
|
||||
eInfo, err := getSREditResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return siteRepApi.NewSiteReplicationEditOK().WithPayload(eInfo)
|
||||
})
|
||||
}
|
||||
|
||||
func getSRInfoResponse(session *models.Principal, params siteRepApi.GetSiteReplicationInfoParams) (*models.SiteReplicationInfoResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
res, err := getSRConfig(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getSRAddResponse(session *models.Principal, params siteRepApi.SiteReplicationInfoAddParams) (*models.SiteReplicationAddResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
res, err := addSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getSREditResponse(session *models.Principal, params siteRepApi.SiteReplicationEditParams) (*models.PeerSiteEditResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
eRes, err := editSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return eRes, nil
|
||||
}
|
||||
|
||||
func getSRRemoveResponse(session *models.Principal, params siteRepApi.SiteReplicationRemoveParams) (*models.PeerSiteRemoveResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
rRes, err := removeSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return rRes, nil
|
||||
}
|
||||
|
||||
func getSRConfig(ctx context.Context, client MinioAdmin) (info *models.SiteReplicationInfoResponse, err error) {
|
||||
srInfo, err := client.getSiteReplicationInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sites []*models.PeerInfo
|
||||
|
||||
if len(srInfo.Sites) > 0 {
|
||||
for _, s := range srInfo.Sites {
|
||||
pInfo := &models.PeerInfo{
|
||||
DeploymentID: s.DeploymentID,
|
||||
Endpoint: s.Endpoint,
|
||||
Name: s.Name,
|
||||
}
|
||||
sites = append(sites, pInfo)
|
||||
}
|
||||
}
|
||||
res := &models.SiteReplicationInfoResponse{
|
||||
Enabled: srInfo.Enabled,
|
||||
Name: srInfo.Name,
|
||||
ServiceAccountAccessKey: srInfo.ServiceAccountAccessKey,
|
||||
Sites: sites,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func addSiteReplication(ctx context.Context, client MinioAdmin, params *siteRepApi.SiteReplicationInfoAddParams) (info *models.SiteReplicationAddResponse, err error) {
|
||||
var rSites []madmin.PeerSite
|
||||
|
||||
if len(params.Body) > 0 {
|
||||
for _, aSite := range params.Body {
|
||||
pInfo := &madmin.PeerSite{
|
||||
AccessKey: aSite.AccessKey,
|
||||
Name: aSite.Name,
|
||||
SecretKey: aSite.SecretKey,
|
||||
Endpoint: aSite.Endpoint,
|
||||
}
|
||||
rSites = append(rSites, *pInfo)
|
||||
}
|
||||
}
|
||||
qs := runtime.Values(params.HTTPRequest.URL.Query())
|
||||
_, qhkReplicateILMExpiry, _ := qs.GetOK("replicate-ilm-expiry")
|
||||
var opts madmin.SRAddOptions
|
||||
if qhkReplicateILMExpiry {
|
||||
opts.ReplicateILMExpiry = true
|
||||
}
|
||||
cc, err := client.addSiteReplicationInfo(ctx, rSites, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &models.SiteReplicationAddResponse{
|
||||
ErrorDetail: cc.ErrDetail,
|
||||
InitialSyncErrorMessage: cc.InitialSyncErrorMessage,
|
||||
Status: cc.Status,
|
||||
Success: cc.Success,
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func editSiteReplication(ctx context.Context, client MinioAdmin, params *siteRepApi.SiteReplicationEditParams) (info *models.PeerSiteEditResponse, err error) {
|
||||
peerSiteInfo := &madmin.PeerInfo{
|
||||
Endpoint: params.Body.Endpoint, // only endpoint can be edited.
|
||||
Name: params.Body.Name, // does not get updated.
|
||||
DeploymentID: params.Body.DeploymentID, // readonly
|
||||
}
|
||||
qs := runtime.Values(params.HTTPRequest.URL.Query())
|
||||
_, qhkDisableILMExpiryReplication, _ := qs.GetOK("disable-ilm-expiry-replication")
|
||||
_, qhkEnableILMExpiryReplication, _ := qs.GetOK("enable-ilm-expiry-replication")
|
||||
var opts madmin.SREditOptions
|
||||
if qhkDisableILMExpiryReplication {
|
||||
opts.DisableILMExpiryReplication = true
|
||||
}
|
||||
if qhkEnableILMExpiryReplication {
|
||||
opts.EnableILMExpiryReplication = true
|
||||
}
|
||||
eRes, err := client.editSiteReplicationInfo(ctx, *peerSiteInfo, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
editRes := &models.PeerSiteEditResponse{
|
||||
ErrorDetail: eRes.ErrDetail,
|
||||
Status: eRes.Status,
|
||||
Success: eRes.Success,
|
||||
}
|
||||
return editRes, nil
|
||||
}
|
||||
|
||||
func removeSiteReplication(ctx context.Context, client MinioAdmin, params *siteRepApi.SiteReplicationRemoveParams) (info *models.PeerSiteRemoveResponse, err error) {
|
||||
delAll := params.Body.All
|
||||
siteNames := params.Body.Sites
|
||||
|
||||
var req *madmin.SRRemoveReq
|
||||
if delAll {
|
||||
req = &madmin.SRRemoveReq{
|
||||
RemoveAll: delAll,
|
||||
}
|
||||
} else {
|
||||
req = &madmin.SRRemoveReq{
|
||||
SiteNames: siteNames,
|
||||
RemoveAll: delAll,
|
||||
}
|
||||
}
|
||||
|
||||
rRes, err := client.deleteSiteReplicationInfo(ctx, *req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
removeRes := &models.PeerSiteRemoveResponse{
|
||||
ErrorDetail: rRes.ErrDetail,
|
||||
Status: rRes.Status,
|
||||
}
|
||||
return removeRes, nil
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
// These tests are for AdminAPI Tag based on swagger-console.yml
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetSiteReplicationInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "getSiteReplicationInfo()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
retValueMock := madmin.SiteReplicationInfo{
|
||||
Enabled: true,
|
||||
Name: "site1",
|
||||
Sites: []madmin.PeerInfo{
|
||||
{
|
||||
Endpoint: "http://localhost:9000",
|
||||
Name: "site1",
|
||||
DeploymentID: "12345",
|
||||
},
|
||||
{
|
||||
Endpoint: "http://localhost:9001",
|
||||
Name: "site2",
|
||||
DeploymentID: "123456",
|
||||
},
|
||||
},
|
||||
ServiceAccountAccessKey: "test-key",
|
||||
}
|
||||
|
||||
expValueMock := &madmin.SiteReplicationInfo{
|
||||
Enabled: true,
|
||||
Name: "site1",
|
||||
Sites: []madmin.PeerInfo{
|
||||
{
|
||||
Endpoint: "http://localhost:9000",
|
||||
Name: "site1",
|
||||
DeploymentID: "12345",
|
||||
},
|
||||
{
|
||||
Endpoint: "http://localhost:9001",
|
||||
Name: "site2",
|
||||
DeploymentID: "123456",
|
||||
},
|
||||
},
|
||||
ServiceAccountAccessKey: "test-key",
|
||||
}
|
||||
|
||||
getSiteReplicationInfo = func(_ context.Context) (info *madmin.SiteReplicationInfo, err error) {
|
||||
return &retValueMock, nil
|
||||
}
|
||||
|
||||
srInfo, err := adminClient.getSiteReplicationInfo(ctx)
|
||||
assert.Nil(err)
|
||||
assert.Equal(expValueMock, srInfo, fmt.Sprintf("Failed on %s: length of lists is not the same", function))
|
||||
}
|
||||
|
||||
func TestAddSiteReplicationInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "addSiteReplicationInfo()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
retValueMock := &madmin.ReplicateAddStatus{
|
||||
Success: true,
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
InitialSyncErrorMessage: "",
|
||||
}
|
||||
|
||||
expValueMock := &madmin.ReplicateAddStatus{
|
||||
Success: true,
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
InitialSyncErrorMessage: "",
|
||||
}
|
||||
|
||||
addSiteReplicationInfo = func(_ context.Context, _ []madmin.PeerSite) (res *madmin.ReplicateAddStatus, err error) {
|
||||
return retValueMock, nil
|
||||
}
|
||||
|
||||
sites := []madmin.PeerSite{
|
||||
{
|
||||
Name: "site1",
|
||||
Endpoint: "http://localhost:9000",
|
||||
AccessKey: "test",
|
||||
SecretKey: "test",
|
||||
},
|
||||
{
|
||||
Name: "site2",
|
||||
Endpoint: "http://localhost:9001",
|
||||
AccessKey: "test",
|
||||
SecretKey: "test",
|
||||
},
|
||||
}
|
||||
|
||||
srInfo, err := adminClient.addSiteReplicationInfo(ctx, sites, madmin.SRAddOptions{})
|
||||
assert.Nil(err)
|
||||
assert.Equal(expValueMock, srInfo, fmt.Sprintf("Failed on %s: length of lists is not the same", function))
|
||||
}
|
||||
|
||||
func TestEditSiteReplicationInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "editSiteReplicationInfo()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
retValueMock := &madmin.ReplicateEditStatus{
|
||||
Success: true,
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
}
|
||||
|
||||
expValueMock := &madmin.ReplicateEditStatus{
|
||||
Success: true,
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
}
|
||||
|
||||
editSiteReplicationInfo = func(_ context.Context, _ madmin.PeerInfo) (res *madmin.ReplicateEditStatus, err error) {
|
||||
return retValueMock, nil
|
||||
}
|
||||
|
||||
site := madmin.PeerInfo{
|
||||
Name: "",
|
||||
Endpoint: "",
|
||||
DeploymentID: "12345",
|
||||
}
|
||||
|
||||
srInfo, err := adminClient.editSiteReplicationInfo(ctx, site, madmin.SREditOptions{})
|
||||
assert.Nil(err)
|
||||
assert.Equal(expValueMock, srInfo, fmt.Sprintf("Failed on %s: length of lists is not the same", function))
|
||||
}
|
||||
|
||||
func TestDeleteSiteReplicationInfo(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "deleteSiteReplicationInfo()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
retValueMock := &madmin.ReplicateRemoveStatus{
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
}
|
||||
|
||||
expValueMock := &madmin.ReplicateRemoveStatus{
|
||||
Status: "success",
|
||||
ErrDetail: "",
|
||||
}
|
||||
|
||||
deleteSiteReplicationInfoMock = func(_ context.Context, _ madmin.SRRemoveReq) (res *madmin.ReplicateRemoveStatus, err error) {
|
||||
return retValueMock, nil
|
||||
}
|
||||
|
||||
remReq := madmin.SRRemoveReq{
|
||||
SiteNames: []string{
|
||||
"test1",
|
||||
},
|
||||
RemoveAll: false,
|
||||
}
|
||||
|
||||
srInfo, err := adminClient.deleteSiteReplicationInfo(ctx, remReq)
|
||||
assert.Nil(err)
|
||||
assert.Equal(expValueMock, srInfo, fmt.Sprintf("Failed on %s: length of lists is not the same", function))
|
||||
}
|
||||
|
||||
func TestSiteReplicationStatus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "getSiteReplicationStatus()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
retValueMock := madmin.SRStatusInfo{
|
||||
Enabled: true,
|
||||
MaxBuckets: 0,
|
||||
MaxUsers: 0,
|
||||
MaxGroups: 0,
|
||||
MaxPolicies: 0,
|
||||
Sites: nil,
|
||||
StatsSummary: nil,
|
||||
BucketStats: nil,
|
||||
PolicyStats: nil,
|
||||
UserStats: nil,
|
||||
GroupStats: nil,
|
||||
}
|
||||
|
||||
expValueMock := &madmin.SRStatusInfo{
|
||||
Enabled: true,
|
||||
MaxBuckets: 0,
|
||||
MaxUsers: 0,
|
||||
MaxGroups: 0,
|
||||
MaxPolicies: 0,
|
||||
Sites: nil,
|
||||
StatsSummary: nil,
|
||||
BucketStats: nil,
|
||||
PolicyStats: nil,
|
||||
UserStats: nil,
|
||||
GroupStats: nil,
|
||||
}
|
||||
|
||||
getSiteReplicationStatus = func(_ context.Context, _ madmin.SRStatusOptions) (info *madmin.SRStatusInfo, err error) {
|
||||
return &retValueMock, nil
|
||||
}
|
||||
|
||||
reqValues := madmin.SRStatusOptions{
|
||||
Buckets: true,
|
||||
Policies: true,
|
||||
Users: true,
|
||||
Groups: true,
|
||||
}
|
||||
srInfo, err := adminClient.getSiteReplicationStatus(ctx, reqValues)
|
||||
if err != nil {
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
assert.Equal(expValueMock, srInfo, fmt.Sprintf("Failed on %s: expected result is not same", function))
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
// getSpeedtesthOptionsFromReq gets duration, size & concurrent requests from a websocket
|
||||
// path come as : `/speedtest?duration=2h&size=12MiB&concurrent=10`
|
||||
func getSpeedtestOptionsFromReq(req *http.Request) (*madmin.SpeedtestOpts, error) {
|
||||
optionsSet := madmin.SpeedtestOpts{}
|
||||
|
||||
queryPairs := req.URL.Query()
|
||||
|
||||
paramDuration := queryPairs.Get("duration")
|
||||
|
||||
if paramDuration == "" {
|
||||
paramDuration = "10s"
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(paramDuration)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse duration: %s", paramDuration)
|
||||
}
|
||||
|
||||
if duration <= 0 {
|
||||
return nil, fmt.Errorf("duration cannot be 0 or negative")
|
||||
}
|
||||
|
||||
optionsSet.Duration = duration
|
||||
|
||||
paramSize := queryPairs.Get("size")
|
||||
|
||||
if paramSize == "" {
|
||||
paramSize = "64MiB"
|
||||
}
|
||||
|
||||
size, err := humanize.ParseBytes(paramSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse object size")
|
||||
}
|
||||
|
||||
optionsSet.Size = int(size)
|
||||
|
||||
paramConcurrent := queryPairs.Get("concurrent")
|
||||
|
||||
if paramConcurrent == "" {
|
||||
paramConcurrent = "32"
|
||||
}
|
||||
|
||||
concurrent, err := strconv.Atoi(paramConcurrent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid concurrent value: %s", paramConcurrent)
|
||||
}
|
||||
|
||||
if concurrent <= 0 {
|
||||
return nil, fmt.Errorf("concurrency cannot be '0' or negative")
|
||||
}
|
||||
|
||||
optionsSet.Concurrency = concurrent
|
||||
|
||||
autotune := queryPairs.Get("autotune")
|
||||
|
||||
if autotune == "true" {
|
||||
optionsSet.Autotune = true
|
||||
}
|
||||
|
||||
return &optionsSet, nil
|
||||
}
|
||||
|
||||
func startSpeedtest(ctx context.Context, conn WSConn, client MinioAdmin, speedtestOpts *madmin.SpeedtestOpts) error {
|
||||
speedtestRes, err := client.speedtest(ctx, *speedtestOpts)
|
||||
if err != nil {
|
||||
LogError("error initializing speedtest: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for result := range speedtestRes {
|
||||
// Serializing message
|
||||
bytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
LogError("error serializing json: %v", err)
|
||||
return err
|
||||
}
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
LogError("error writing speedtest response: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,435 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
subnetApi "github.com/minio/console/api/operations/subnet"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/subnet"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func registerSubnetHandlers(api *operations.ConsoleAPI) {
|
||||
// Get subnet login handler
|
||||
api.SubnetSubnetLoginHandler = subnetApi.SubnetLoginHandlerFunc(func(params subnetApi.SubnetLoginParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetLoginResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetLoginDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetLoginOK().WithPayload(resp)
|
||||
})
|
||||
// Get subnet login with MFA handler
|
||||
api.SubnetSubnetLoginMFAHandler = subnetApi.SubnetLoginMFAHandlerFunc(func(params subnetApi.SubnetLoginMFAParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetLoginWithMFAResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetLoginMFADefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetLoginMFAOK().WithPayload(resp)
|
||||
})
|
||||
// Get subnet register
|
||||
api.SubnetSubnetRegisterHandler = subnetApi.SubnetRegisterHandlerFunc(func(params subnetApi.SubnetRegisterParams, session *models.Principal) middleware.Responder {
|
||||
err := GetSubnetRegisterResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetRegisterDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetRegisterOK()
|
||||
})
|
||||
// Get subnet info
|
||||
api.SubnetSubnetInfoHandler = subnetApi.SubnetInfoHandlerFunc(func(params subnetApi.SubnetInfoParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetInfoOK().WithPayload(resp)
|
||||
})
|
||||
// Get subnet registration token
|
||||
api.SubnetSubnetRegTokenHandler = subnetApi.SubnetRegTokenHandlerFunc(func(params subnetApi.SubnetRegTokenParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetRegTokenResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetRegTokenDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetRegTokenOK().WithPayload(resp)
|
||||
})
|
||||
|
||||
api.SubnetSubnetAPIKeyHandler = subnetApi.SubnetAPIKeyHandlerFunc(func(params subnetApi.SubnetAPIKeyParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetAPIKeyResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetAPIKeyDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return subnetApi.NewSubnetAPIKeyOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
const EnvSubnetLicense = "CONSOLE_SUBNET_LICENSE"
|
||||
|
||||
func SubnetRegisterWithAPIKey(ctx context.Context, minioClient MinioAdmin, apiKey string) (bool, error) {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
registerResult, err := subnet.Register(GetConsoleHTTPClient(clientIP), serverInfo, apiKey, "", "")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Keep existing subnet proxy if exists
|
||||
subnetKey, err := GetSubnetKeyFromMinIOConfig(ctx, minioClient)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
configStr := fmt.Sprintf("subnet license=%s api_key=%s proxy=%s", registerResult.License, registerResult.APIKey, subnetKey.Proxy)
|
||||
_, err = minioClient.setConfigKV(ctx, configStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// cluster registered correctly
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SubnetLogin(client xhttp.ClientI, username, password string) (string, string, error) {
|
||||
tokens, err := subnet.Login(client, username, password)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if tokens.MfaToken != "" {
|
||||
// user needs to complete login flow using mfa
|
||||
return "", tokens.MfaToken, nil
|
||||
}
|
||||
if tokens.AccessToken != "" {
|
||||
// register token to minio
|
||||
return tokens.AccessToken, "", nil
|
||||
}
|
||||
return "", "", errors.New("something went wrong")
|
||||
}
|
||||
|
||||
func GetSubnetLoginResponse(session *models.Principal, params subnetApi.SubnetLoginParams) (*models.SubnetLoginResponse, *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 subnetLoginResponse(ctx, AdminClient{Client: mAdmin}, params)
|
||||
}
|
||||
|
||||
func subnetLoginResponse(ctx context.Context, minioClient MinioAdmin, params subnetApi.SubnetLoginParams) (*models.SubnetLoginResponse, *CodedAPIError) {
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
apiKey := params.Body.APIKey
|
||||
if apiKey != "" {
|
||||
registered, err := SubnetRegisterWithAPIKey(ctx, minioClient, apiKey)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
Registered: registered,
|
||||
Organizations: []*models.SubnetOrganization{},
|
||||
}, nil
|
||||
}
|
||||
username := params.Body.Username
|
||||
password := params.Body.Password
|
||||
if username != "" && password != "" {
|
||||
token, mfa, err := SubnetLogin(subnetHTTPClient, username, password)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
MfaToken: mfa,
|
||||
AccessToken: token,
|
||||
Organizations: []*models.SubnetOrganization{},
|
||||
}, nil
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, ErrDefault)
|
||||
}
|
||||
|
||||
type SubnetRegistration struct {
|
||||
AccessToken string
|
||||
MFAToken string
|
||||
Organizations []models.SubnetOrganization
|
||||
}
|
||||
|
||||
func SubnetLoginWithMFA(client xhttp.ClientI, username, mfaToken, otp string) (*models.SubnetLoginResponse, error) {
|
||||
tokens, err := subnet.LoginWithMFA(client, username, mfaToken, otp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokens.AccessToken != "" {
|
||||
organizations, errOrg := subnet.GetOrganizations(client, tokens.AccessToken)
|
||||
if errOrg != nil {
|
||||
return nil, errOrg
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
AccessToken: tokens.AccessToken,
|
||||
Organizations: organizations,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
|
||||
// GetSubnetHTTPClient will return a client with proxy if configured, otherwise will return the default console http client
|
||||
func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*xhttp.Client, error) {
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
subnetKey, err := GetSubnetKeyFromMinIOConfig(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxy := getSubnetProxy()
|
||||
if subnetKey.Proxy != "" {
|
||||
proxy = subnetKey.Proxy
|
||||
}
|
||||
|
||||
tr := GlobalTransport.Clone()
|
||||
if proxy != "" {
|
||||
u, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr.Proxy = http.ProxyURL(u)
|
||||
}
|
||||
|
||||
return &xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Transport: &ConsoleTransport{
|
||||
Transport: tr,
|
||||
ClientIP: clientIP,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetSubnetLoginWithMFAResponse(session *models.Principal, params subnetApi.SubnetLoginMFAParams) (*models.SubnetLoginResponse, *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)
|
||||
}
|
||||
minioClient := AdminClient{Client: mAdmin}
|
||||
return subnetLoginWithMFAResponse(ctx, minioClient, params)
|
||||
}
|
||||
|
||||
func subnetLoginWithMFAResponse(ctx context.Context, minioClient MinioAdmin, params subnetApi.SubnetLoginMFAParams) (*models.SubnetLoginResponse, *CodedAPIError) {
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
resp, err := SubnetLoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func GetSubnetKeyFromMinIOConfig(ctx context.Context, minioClient MinioAdmin) (*subnet.LicenseTokenConfig, error) {
|
||||
buf, err := minioClient.getConfigKV(ctx, madmin.SubnetSubSys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subSysConfigs, err := madmin.ParseServerConfigOutput(string(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, scfg := range subSysConfigs {
|
||||
if scfg.Target == "" {
|
||||
res := subnet.LicenseTokenConfig{}
|
||||
res.APIKey, _ = scfg.Lookup("api_key")
|
||||
res.License, _ = scfg.Lookup("license")
|
||||
res.Proxy, _ = scfg.Lookup("proxy")
|
||||
return &res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("unable to find subnet configuration")
|
||||
}
|
||||
|
||||
func GetSubnetRegister(ctx context.Context, minioClient MinioAdmin, httpClient xhttp.ClientI, params subnetApi.SubnetRegisterParams) error {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registerResult, err := subnet.Register(httpClient, serverInfo, "", *params.Body.Token, *params.Body.AccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Keep existing subnet proxy if exists
|
||||
subnetKey, err := GetSubnetKeyFromMinIOConfig(ctx, minioClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := fmt.Sprintf("subnet license=%s api_key=%s proxy=%s", registerResult.License, registerResult.APIKey, subnetKey.Proxy)
|
||||
_, err = minioClient.setConfigKV(ctx, configStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSubnetRegisterResponse(session *models.Principal, params subnetApi.SubnetRegisterParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
return subnetRegisterResponse(ctx, adminClient, params)
|
||||
}
|
||||
|
||||
func subnetRegisterResponse(ctx context.Context, minioClient MinioAdmin, params subnetApi.SubnetRegisterParams) *CodedAPIError {
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
err = GetSubnetRegister(ctx, minioClient, subnetHTTPClient, params)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrSubnetLicenseNotFound = errors.New("license not found")
|
||||
|
||||
func GetSubnetInfoResponse(session *models.Principal, params subnetApi.SubnetInfoParams) (*models.License, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientIP := utils.ClientIPFromContext(ctx)
|
||||
client := &xhttp.Client{
|
||||
Client: GetConsoleHTTPClient(clientIP),
|
||||
}
|
||||
// license gets seeded to us by MinIO
|
||||
seededLicense := os.Getenv(EnvSubnetLicense)
|
||||
// if it's missing, we will gracefully fallback to attempt to fetch it from MinIO
|
||||
if seededLicense == "" {
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
configBytes, err := adminClient.getConfigKV(params.HTTPRequest.Context(), "subnet")
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
subSysConfigs, err := madmin.ParseServerConfigOutput(string(configBytes))
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// search for licese
|
||||
for _, v := range subSysConfigs {
|
||||
for _, sv := range v.KV {
|
||||
if sv.Key == "license" {
|
||||
seededLicense = sv.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// still empty means not found
|
||||
if seededLicense == "" {
|
||||
return nil, ErrorWithContext(ctx, ErrSubnetLicenseNotFound)
|
||||
}
|
||||
|
||||
licenseInfo, err := getLicenseInfo(*client.Client, seededLicense)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
license := &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}
|
||||
return license, nil
|
||||
}
|
||||
|
||||
func GetSubnetRegToken(ctx context.Context, minioClient MinioAdmin) (string, error) {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
regInfo := subnet.GetClusterRegInfo(serverInfo)
|
||||
regToken, err := subnet.GenerateRegToken(regInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return regToken, nil
|
||||
}
|
||||
|
||||
func GetSubnetRegTokenResponse(session *models.Principal, params subnetApi.SubnetRegTokenParams) (*models.SubnetRegTokenResponse, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
return subnetRegTokenResponse(ctx, adminClient)
|
||||
}
|
||||
|
||||
func subnetRegTokenResponse(ctx context.Context, minioClient MinioAdmin) (*models.SubnetRegTokenResponse, *CodedAPIError) {
|
||||
token, err := GetSubnetRegToken(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetRegTokenResponse{
|
||||
RegToken: token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetSubnetAPIKeyResponse(session *models.Principal, params subnetApi.SubnetAPIKeyParams) (*models.APIKey, *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)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
return subnetAPIKeyResponse(ctx, adminClient, params)
|
||||
}
|
||||
|
||||
func subnetAPIKeyResponse(ctx context.Context, minioClient MinioAdmin, params subnetApi.SubnetAPIKeyParams) (*models.APIKey, *CodedAPIError) {
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
token := params.HTTPRequest.URL.Query().Get("token")
|
||||
apiKey, err := subnet.GetAPIKey(subnetHTTPClient, token)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.APIKey{APIKey: apiKey}, nil
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
// 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"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/api/operations"
|
||||
subnetApi "github.com/minio/console/api/operations/subnet"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type AdminSubnetTestSuite struct {
|
||||
suite.Suite
|
||||
assert *assert.Assertions
|
||||
currentServer string
|
||||
isServerSet bool
|
||||
server *httptest.Server
|
||||
adminClient AdminClientMock
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) SetupSuite() {
|
||||
suite.assert = assert.New(suite.T())
|
||||
suite.adminClient = AdminClientMock{}
|
||||
minioGetConfigKVMock = func(_ string) ([]byte, error) {
|
||||
return []byte("subnet license=mock api_key=mock proxy=http://mock.com"), nil
|
||||
}
|
||||
MinioServerInfoMock = func(_ context.Context) (madmin.InfoMessage, error) {
|
||||
return madmin.InfoMessage{Servers: []madmin.ServerProperties{{}}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) SetupTest() {
|
||||
suite.server = httptest.NewServer(http.HandlerFunc(suite.serverHandler))
|
||||
suite.currentServer, suite.isServerSet = os.LookupEnv(ConsoleMinIOServer)
|
||||
os.Setenv(ConsoleMinIOServer, suite.server.URL)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) serverHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(400)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TearDownTest() {
|
||||
if suite.isServerSet {
|
||||
os.Setenv(ConsoleMinIOServer, suite.currentServer)
|
||||
} else {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestRegisterSubnetHandlers() {
|
||||
api := &operations.ConsoleAPI{}
|
||||
suite.assertHandlersAreNil(api)
|
||||
registerSubnetHandlers(api)
|
||||
suite.assertHandlersAreNotNil(api)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) assertHandlersAreNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.Nil(api.SubnetSubnetLoginHandler)
|
||||
suite.assert.Nil(api.SubnetSubnetLoginMFAHandler)
|
||||
suite.assert.Nil(api.SubnetSubnetRegisterHandler)
|
||||
suite.assert.Nil(api.SubnetSubnetInfoHandler)
|
||||
suite.assert.Nil(api.SubnetSubnetRegTokenHandler)
|
||||
suite.assert.Nil(api.SubnetSubnetAPIKeyHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) assertHandlersAreNotNil(api *operations.ConsoleAPI) {
|
||||
suite.assert.NotNil(api.SubnetSubnetLoginHandler)
|
||||
suite.assert.NotNil(api.SubnetSubnetLoginMFAHandler)
|
||||
suite.assert.NotNil(api.SubnetSubnetRegisterHandler)
|
||||
suite.assert.NotNil(api.SubnetSubnetInfoHandler)
|
||||
suite.assert.NotNil(api.SubnetSubnetRegTokenHandler)
|
||||
suite.assert.NotNil(api.SubnetSubnetAPIKeyHandler)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetLoginWithSubnetClientError() {
|
||||
params, api := suite.initSubnetLoginRequest("", "", "")
|
||||
response := api.SubnetSubnetLoginHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetLoginDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetLoginResponseWithApiKeyError() {
|
||||
params, _ := suite.initSubnetLoginRequest("mock", "", "")
|
||||
res, err := subnetLoginResponse(context.TODO(), suite.adminClient, params)
|
||||
suite.assert.NotNil(err)
|
||||
suite.assert.Nil(res)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetLoginResponseWithCredentialsError() {
|
||||
params, _ := suite.initSubnetLoginRequest("", "mock", "mock")
|
||||
res, err := subnetLoginResponse(context.TODO(), suite.adminClient, params)
|
||||
suite.assert.NotNil(err)
|
||||
suite.assert.Nil(res)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetLoginRequest(apiKey, username, password string) (params subnetApi.SubnetLoginParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.SubnetLoginRequest{}
|
||||
params.Body.APIKey = apiKey
|
||||
params.Body.Username = username
|
||||
params.Body.Password = password
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetLoginMFAWithSubnetClientError() {
|
||||
params, api := suite.initSubnetLoginMFARequest("", "", "")
|
||||
response := api.SubnetSubnetLoginMFAHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetLoginMFADefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetLoginWithMFAResponseError() {
|
||||
params, _ := suite.initSubnetLoginMFARequest("mock", "mock", "mock")
|
||||
res, err := subnetLoginWithMFAResponse(context.TODO(), suite.adminClient, params)
|
||||
suite.assert.NotNil(err)
|
||||
suite.assert.Nil(res)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetLoginMFARequest(username, mfaToken, otp string) (params subnetApi.SubnetLoginMFAParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.SubnetLoginMFARequest{}
|
||||
params.Body.Username = &username
|
||||
params.Body.MfaToken = &mfaToken
|
||||
params.Body.Otp = &otp
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetRegisterClientError() {
|
||||
params, api := suite.initSubnetRegisterRequest("", "")
|
||||
response := api.SubnetSubnetRegisterHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetRegisterDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetRegisterResponseError() {
|
||||
params, _ := suite.initSubnetRegisterRequest("mock", "mock")
|
||||
err := subnetRegisterResponse(context.TODO(), suite.adminClient, params)
|
||||
suite.assert.NotNil(err)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetRegisterRequest(token, accountID string) (params subnetApi.SubnetRegisterParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.Body = &models.SubnetRegisterRequest{}
|
||||
params.Body.Token = &token
|
||||
params.Body.AccountID = &accountID
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetInfoError() {
|
||||
params, api := suite.initSubnetInfoRequest()
|
||||
response := api.SubnetSubnetInfoHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetInfoDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetInfoRequest() (params subnetApi.SubnetInfoParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetRegTokenError() {
|
||||
params, api := suite.initSubnetRegTokenRequest()
|
||||
response := api.SubnetSubnetRegTokenHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetRegTokenDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetRegTokenResponse() {
|
||||
res, err := subnetRegTokenResponse(context.TODO(), suite.adminClient)
|
||||
suite.assert.Nil(err)
|
||||
suite.assert.NotEqual("", res)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetRegTokenRequest() (params subnetApi.SubnetRegTokenParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetAPIKeyWithClientError() {
|
||||
params, api := suite.initSubnetAPIKeyRequest()
|
||||
response := api.SubnetSubnetAPIKeyHandler.Handle(params, &models.Principal{})
|
||||
_, ok := response.(*subnetApi.SubnetAPIKeyDefault)
|
||||
suite.assert.True(ok)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) TestSubnetAPIKeyResponseError() {
|
||||
params, _ := suite.initSubnetAPIKeyRequest()
|
||||
res, err := subnetAPIKeyResponse(context.TODO(), suite.adminClient, params)
|
||||
suite.assert.NotNil(err)
|
||||
suite.assert.Nil(res)
|
||||
}
|
||||
|
||||
func (suite *AdminSubnetTestSuite) initSubnetAPIKeyRequest() (params subnetApi.SubnetAPIKeyParams, api operations.ConsoleAPI) {
|
||||
registerSubnetHandlers(&api)
|
||||
params.HTTPRequest = &http.Request{}
|
||||
params.HTTPRequest.URL = &url.URL{}
|
||||
return params, api
|
||||
}
|
||||
|
||||
func TestAdminSubnet(t *testing.T) {
|
||||
suite.Run(t, new(AdminSubnetTestSuite))
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/api/operations"
|
||||
tieringApi "github.com/minio/console/api/operations/tiering"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
)
|
||||
|
||||
func registerAdminTiersHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of notification endpoints
|
||||
api.TieringTiersListHandler = tieringApi.TiersListHandlerFunc(func(params tieringApi.TiersListParams, session *models.Principal) middleware.Responder {
|
||||
tierList, err := getTiersResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewTiersListDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return tieringApi.NewTiersListOK().WithPayload(tierList)
|
||||
})
|
||||
// add a new tiers
|
||||
api.TieringAddTierHandler = tieringApi.AddTierHandlerFunc(func(params tieringApi.AddTierParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddTierResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewAddTierDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return tieringApi.NewAddTierCreated()
|
||||
})
|
||||
// get a tier
|
||||
api.TieringGetTierHandler = tieringApi.GetTierHandlerFunc(func(params tieringApi.GetTierParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getGetTierResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewGetTierDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return tieringApi.NewGetTierOK().WithPayload(notifEndpoints)
|
||||
})
|
||||
// edit credentials for a tier
|
||||
api.TieringEditTierCredentialsHandler = tieringApi.EditTierCredentialsHandlerFunc(func(params tieringApi.EditTierCredentialsParams, session *models.Principal) middleware.Responder {
|
||||
err := getEditTierCredentialsResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewEditTierCredentialsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return tieringApi.NewEditTierCredentialsOK()
|
||||
})
|
||||
}
|
||||
|
||||
// getNotificationEndpoints invokes admin info and returns a list of notification endpoints
|
||||
func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, error) {
|
||||
tiers, err := client.listTiers(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tiersInfo, err := client.tierStats(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tiersList []*models.Tier
|
||||
for _, tierData := range tiers {
|
||||
|
||||
// Default Tier Stats
|
||||
stats := madmin.TierStats{
|
||||
NumObjects: 0,
|
||||
NumVersions: 0,
|
||||
TotalSize: 0,
|
||||
}
|
||||
|
||||
// We look for the correct tier stats & set the values.
|
||||
for _, stat := range tiersInfo {
|
||||
if stat.Name == tierData.Name {
|
||||
stats = stat.Stats
|
||||
break
|
||||
}
|
||||
}
|
||||
switch tierData.Type {
|
||||
case madmin.S3:
|
||||
tiersList = append(tiersList, &models.Tier{
|
||||
Type: models.TierTypeS3,
|
||||
S3: &models.TierS3{
|
||||
Accesskey: tierData.S3.AccessKey,
|
||||
Bucket: tierData.S3.Bucket,
|
||||
Endpoint: tierData.S3.Endpoint,
|
||||
Name: tierData.Name,
|
||||
Prefix: tierData.S3.Prefix,
|
||||
Region: tierData.S3.Region,
|
||||
Secretkey: tierData.S3.SecretKey,
|
||||
Storageclass: tierData.S3.StorageClass,
|
||||
Usage: humanize.IBytes(stats.TotalSize),
|
||||
Objects: strconv.Itoa(stats.NumObjects),
|
||||
Versions: strconv.Itoa(stats.NumVersions),
|
||||
},
|
||||
Status: client.verifyTierStatus(ctx, tierData.Name) == nil,
|
||||
})
|
||||
case madmin.MinIO:
|
||||
tiersList = append(tiersList, &models.Tier{
|
||||
Type: models.TierTypeMinio,
|
||||
Minio: &models.TierMinio{
|
||||
Accesskey: tierData.MinIO.AccessKey,
|
||||
Bucket: tierData.MinIO.Bucket,
|
||||
Endpoint: tierData.MinIO.Endpoint,
|
||||
Name: tierData.Name,
|
||||
Prefix: tierData.MinIO.Prefix,
|
||||
Region: tierData.MinIO.Region,
|
||||
Secretkey: tierData.MinIO.SecretKey,
|
||||
Usage: humanize.IBytes(stats.TotalSize),
|
||||
Objects: strconv.Itoa(stats.NumObjects),
|
||||
Versions: strconv.Itoa(stats.NumVersions),
|
||||
},
|
||||
Status: client.verifyTierStatus(ctx, tierData.Name) == nil,
|
||||
})
|
||||
case madmin.GCS:
|
||||
tiersList = append(tiersList, &models.Tier{
|
||||
Type: models.TierTypeGcs,
|
||||
Gcs: &models.TierGcs{
|
||||
Bucket: tierData.GCS.Bucket,
|
||||
Creds: tierData.GCS.Creds,
|
||||
Endpoint: tierData.GCS.Endpoint,
|
||||
Name: tierData.Name,
|
||||
Prefix: tierData.GCS.Prefix,
|
||||
Region: tierData.GCS.Region,
|
||||
Usage: humanize.IBytes(stats.TotalSize),
|
||||
Objects: strconv.Itoa(stats.NumObjects),
|
||||
Versions: strconv.Itoa(stats.NumVersions),
|
||||
},
|
||||
Status: client.verifyTierStatus(ctx, tierData.Name) == nil,
|
||||
})
|
||||
case madmin.Azure:
|
||||
tiersList = append(tiersList, &models.Tier{
|
||||
Type: models.TierTypeAzure,
|
||||
Azure: &models.TierAzure{
|
||||
Accountkey: tierData.Azure.AccountKey,
|
||||
Accountname: tierData.Azure.AccountName,
|
||||
Bucket: tierData.Azure.Bucket,
|
||||
Endpoint: tierData.Azure.Endpoint,
|
||||
Name: tierData.Name,
|
||||
Prefix: tierData.Azure.Prefix,
|
||||
Region: tierData.Azure.Region,
|
||||
Usage: humanize.IBytes(stats.TotalSize),
|
||||
Objects: strconv.Itoa(stats.NumObjects),
|
||||
Versions: strconv.Itoa(stats.NumVersions),
|
||||
},
|
||||
Status: client.verifyTierStatus(ctx, tierData.Name) == nil,
|
||||
})
|
||||
case madmin.Unsupported:
|
||||
tiersList = append(tiersList, &models.Tier{
|
||||
Type: models.TierTypeUnsupported,
|
||||
Status: client.verifyTierStatus(ctx, tierData.Name) == nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
// build response
|
||||
return &models.TierListResponse{
|
||||
Items: tiersList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getTiersResponse returns a response with a list of tiers
|
||||
func getTiersResponse(session *models.Principal, params tieringApi.TiersListParams) (*models.TierListResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
tiersResp, err := getTiers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return tiersResp, nil
|
||||
}
|
||||
|
||||
func addTier(ctx context.Context, client MinioAdmin, params *tieringApi.AddTierParams) error {
|
||||
var cfg *madmin.TierConfig
|
||||
var err error
|
||||
|
||||
switch params.Body.Type {
|
||||
|
||||
case models.TierTypeS3:
|
||||
cfg, err = madmin.NewTierS3(
|
||||
params.Body.S3.Name,
|
||||
params.Body.S3.Accesskey,
|
||||
params.Body.S3.Secretkey,
|
||||
params.Body.S3.Bucket,
|
||||
madmin.S3Region(params.Body.S3.Region),
|
||||
madmin.S3Prefix(params.Body.S3.Prefix),
|
||||
madmin.S3Endpoint(params.Body.S3.Endpoint),
|
||||
madmin.S3StorageClass(params.Body.S3.Storageclass),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case models.TierTypeMinio:
|
||||
cfg, err = madmin.NewTierMinIO(
|
||||
params.Body.Minio.Name,
|
||||
params.Body.Minio.Endpoint,
|
||||
params.Body.Minio.Accesskey,
|
||||
params.Body.Minio.Secretkey,
|
||||
params.Body.Minio.Bucket,
|
||||
madmin.MinIORegion(params.Body.Minio.Region),
|
||||
madmin.MinIOPrefix(params.Body.Minio.Prefix),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case models.TierTypeGcs:
|
||||
gcsOpts := []madmin.GCSOptions{}
|
||||
prefix := params.Body.Gcs.Prefix
|
||||
if prefix != "" {
|
||||
gcsOpts = append(gcsOpts, madmin.GCSPrefix(prefix))
|
||||
}
|
||||
|
||||
region := params.Body.Gcs.Region
|
||||
if region != "" {
|
||||
gcsOpts = append(gcsOpts, madmin.GCSRegion(region))
|
||||
}
|
||||
base64Text := make([]byte, base64.StdEncoding.EncodedLen(len(params.Body.Gcs.Creds)))
|
||||
l, _ := base64.StdEncoding.Decode(base64Text, []byte(params.Body.Gcs.Creds))
|
||||
|
||||
cfg, err = madmin.NewTierGCS(
|
||||
params.Body.Gcs.Name,
|
||||
base64Text[:l],
|
||||
params.Body.Gcs.Bucket,
|
||||
gcsOpts...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case models.TierTypeAzure:
|
||||
cfg, err = madmin.NewTierAzure(
|
||||
params.Body.Azure.Name,
|
||||
params.Body.Azure.Accountname,
|
||||
params.Body.Azure.Accountkey,
|
||||
params.Body.Azure.Bucket,
|
||||
madmin.AzurePrefix(params.Body.Azure.Prefix),
|
||||
madmin.AzureEndpoint(params.Body.Azure.Endpoint),
|
||||
madmin.AzureRegion(params.Body.Azure.Region),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case models.TierTypeUnsupported:
|
||||
cfg = &madmin.TierConfig{
|
||||
Type: madmin.Unsupported,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = client.addTier(ctx, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAddTierResponse returns the response of admin tier
|
||||
func getAddTierResponse(session *models.Principal, params tieringApi.AddTierParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
// serialize output
|
||||
errTier := addTier(ctx, adminClient, ¶ms)
|
||||
if errTier != nil {
|
||||
return ErrorWithContext(ctx, errTier)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTier(ctx context.Context, client MinioAdmin, params *tieringApi.GetTierParams) (*models.Tier, error) {
|
||||
tiers, err := client.listTiers(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range tiers {
|
||||
switch tiers[i].Type {
|
||||
case madmin.S3:
|
||||
if params.Type != models.TierTypeS3 || tiers[i].Name != params.Name {
|
||||
continue
|
||||
}
|
||||
return &models.Tier{
|
||||
Type: models.TierTypeS3,
|
||||
S3: &models.TierS3{
|
||||
Accesskey: tiers[i].S3.AccessKey,
|
||||
Bucket: tiers[i].S3.Bucket,
|
||||
Endpoint: tiers[i].S3.Endpoint,
|
||||
Name: tiers[i].Name,
|
||||
Prefix: tiers[i].S3.Prefix,
|
||||
Region: tiers[i].S3.Region,
|
||||
Secretkey: tiers[i].S3.SecretKey,
|
||||
Storageclass: tiers[i].S3.StorageClass,
|
||||
},
|
||||
}, err
|
||||
case madmin.GCS:
|
||||
if params.Type != models.TierTypeGcs || tiers[i].Name != params.Name {
|
||||
continue
|
||||
}
|
||||
return &models.Tier{
|
||||
Type: models.TierTypeGcs,
|
||||
Gcs: &models.TierGcs{
|
||||
Bucket: tiers[i].GCS.Bucket,
|
||||
Creds: tiers[i].GCS.Creds,
|
||||
Endpoint: tiers[i].GCS.Endpoint,
|
||||
Name: tiers[i].Name,
|
||||
Prefix: tiers[i].GCS.Prefix,
|
||||
Region: tiers[i].GCS.Region,
|
||||
},
|
||||
}, nil
|
||||
case madmin.Azure:
|
||||
if params.Type != models.TierTypeAzure || tiers[i].Name != params.Name {
|
||||
continue
|
||||
}
|
||||
return &models.Tier{
|
||||
Type: models.TierTypeAzure,
|
||||
Azure: &models.TierAzure{
|
||||
Accountkey: tiers[i].Azure.AccountKey,
|
||||
Accountname: tiers[i].Azure.AccountName,
|
||||
Bucket: tiers[i].Azure.Bucket,
|
||||
Endpoint: tiers[i].Azure.Endpoint,
|
||||
Name: tiers[i].Name,
|
||||
Prefix: tiers[i].Azure.Prefix,
|
||||
Region: tiers[i].Azure.Region,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// build response
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// getGetTierResponse returns a tier
|
||||
func getGetTierResponse(session *models.Principal, params tieringApi.GetTierParams) (*models.Tier, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
addTierResp, err := getTier(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return addTierResp, nil
|
||||
}
|
||||
|
||||
func editTierCredentials(ctx context.Context, client MinioAdmin, params *tieringApi.EditTierCredentialsParams) error {
|
||||
base64Text := make([]byte, base64.StdEncoding.EncodedLen(len(params.Body.Creds)))
|
||||
l, err := base64.StdEncoding.Decode(base64Text, []byte(params.Body.Creds))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
creds := madmin.TierCreds{
|
||||
AccessKey: params.Body.AccessKey,
|
||||
SecretKey: params.Body.SecretKey,
|
||||
CredsJSON: base64Text[:l],
|
||||
}
|
||||
return client.editTierCreds(ctx, params.Name, creds)
|
||||
}
|
||||
|
||||
// getEditTierCredentialsResponse returns the result of editing credentials for a tier
|
||||
func getEditTierCredentialsResponse(session *models.Principal, params tieringApi.EditTierCredentialsParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// serialize output
|
||||
err = editTierCredentials(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
tieringApi "github.com/minio/console/api/operations/tiering"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTiers(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "getTiers()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : getBucketLifecycle() get list of tiers
|
||||
// mock lifecycle response from MinIO
|
||||
returnListMock := []*madmin.TierConfig{
|
||||
{
|
||||
Version: "V1",
|
||||
Type: madmin.TierType(0),
|
||||
Name: "S3 Tier",
|
||||
S3: &madmin.TierS3{
|
||||
Endpoint: "https://s3tier.test.com/",
|
||||
AccessKey: "Access Key",
|
||||
SecretKey: "Secret Key",
|
||||
Bucket: "buckets3",
|
||||
Prefix: "pref1",
|
||||
Region: "us-west-1",
|
||||
StorageClass: "TT1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
returnStatsMock := []madmin.TierInfo{
|
||||
{
|
||||
Name: "STANDARD",
|
||||
Type: "internal",
|
||||
Stats: madmin.TierStats{NumObjects: 2, NumVersions: 2, TotalSize: 228915},
|
||||
},
|
||||
{
|
||||
Name: "S3 Tier",
|
||||
Type: "s3",
|
||||
Stats: madmin.TierStats{NumObjects: 0, NumVersions: 0, TotalSize: 0},
|
||||
},
|
||||
}
|
||||
|
||||
expectedOutput := &models.TierListResponse{
|
||||
Items: []*models.Tier{
|
||||
{
|
||||
Type: "S3",
|
||||
S3: &models.TierS3{
|
||||
Accesskey: "Access Key",
|
||||
Secretkey: "Secret Key",
|
||||
Bucket: "buckets3",
|
||||
Endpoint: "https://s3tier.test.com/",
|
||||
Name: "S3 Tier",
|
||||
Prefix: "pref1",
|
||||
Region: "us-west-1",
|
||||
Storageclass: "TT1",
|
||||
Usage: "0 B",
|
||||
Objects: "0",
|
||||
Versions: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
minioListTiersMock = func(_ context.Context) ([]*madmin.TierConfig, error) {
|
||||
return returnListMock, nil
|
||||
}
|
||||
|
||||
minioTierStatsMock = func(_ context.Context) ([]madmin.TierInfo, error) {
|
||||
return returnStatsMock, nil
|
||||
}
|
||||
|
||||
tiersList, err := getTiers(ctx, adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// verify length of tiers list is correct
|
||||
assert.Equal(len(tiersList.Items), len(returnListMock), fmt.Sprintf("Failed on %s: length of lists is not the same", function))
|
||||
for i, conf := range returnListMock {
|
||||
switch conf.Type {
|
||||
case madmin.TierType(0):
|
||||
// S3
|
||||
assert.Equal(expectedOutput.Items[i].S3.Name, conf.Name)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Bucket, conf.S3.Bucket)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Prefix, conf.S3.Prefix)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Accesskey, conf.S3.AccessKey)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Secretkey, conf.S3.SecretKey)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Endpoint, conf.S3.Endpoint)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Region, conf.S3.Region)
|
||||
assert.Equal(expectedOutput.Items[i].S3.Storageclass, conf.S3.StorageClass)
|
||||
case madmin.TierType(1):
|
||||
// Azure
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Name, conf.Name)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Bucket, conf.Azure.Bucket)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Prefix, conf.Azure.Prefix)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Accountkey, conf.Azure.AccountKey)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Accountname, conf.Azure.AccountName)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Endpoint, conf.Azure.Endpoint)
|
||||
assert.Equal(expectedOutput.Items[i].Azure.Region, conf.Azure.Region)
|
||||
case madmin.TierType(2):
|
||||
// GCS
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Name, conf.Name)
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Bucket, conf.GCS.Bucket)
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Prefix, conf.GCS.Prefix)
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Creds, conf.GCS.Creds)
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Endpoint, conf.GCS.Endpoint)
|
||||
assert.Equal(expectedOutput.Items[i].Gcs.Region, conf.GCS.Region)
|
||||
}
|
||||
}
|
||||
|
||||
// Test-2 : getBucketLifecycle() list is empty
|
||||
returnListMockT2 := []*madmin.TierConfig{}
|
||||
|
||||
minioListTiersMock = func(_ context.Context) ([]*madmin.TierConfig, error) {
|
||||
return returnListMockT2, nil
|
||||
}
|
||||
|
||||
tiersListT2, err := getTiers(ctx, adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
if len(tiersListT2.Items) != 0 {
|
||||
t.Errorf("Failed on %s:, returned list was not empty", function)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddTier(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "addTier()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: addTier() add new Tier
|
||||
minioAddTiersMock = func(_ context.Context, _ *madmin.TierConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
paramsToAdd := tieringApi.AddTierParams{
|
||||
Body: &models.Tier{
|
||||
Type: "S3",
|
||||
S3: &models.TierS3{
|
||||
Accesskey: "TestAK",
|
||||
Bucket: "bucket1",
|
||||
Endpoint: "https://test.com/",
|
||||
Name: "TIERS3",
|
||||
Prefix: "Pr1",
|
||||
Region: "us-west-1",
|
||||
Secretkey: "SecretK",
|
||||
Storageclass: "STCLASS",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := addTier(ctx, adminClient, ¶msToAdd)
|
||||
assert.Equal(nil, err, fmt.Sprintf("Failed on %s: Error returned", function))
|
||||
|
||||
// Test-2: addTier() error adding Tier
|
||||
minioAddTiersMock = func(_ context.Context, _ *madmin.TierConfig) error {
|
||||
return errors.New("error setting new tier")
|
||||
}
|
||||
|
||||
err2 := addTier(ctx, adminClient, ¶msToAdd)
|
||||
|
||||
assert.Equal(errors.New("error setting new tier"), err2, fmt.Sprintf("Failed on %s: Error returned", function))
|
||||
}
|
||||
|
||||
func TestUpdateTierCreds(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
|
||||
function := "editTierCredentials()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: editTierCredentials() update Tier configuration
|
||||
minioEditTiersMock = func(_ context.Context, _ string, _ madmin.TierCreds) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
params := &tieringApi.EditTierCredentialsParams{
|
||||
Name: "TESTTIER",
|
||||
Body: &models.TierCredentialsRequest{
|
||||
AccessKey: "New Key",
|
||||
SecretKey: "Secret Key",
|
||||
},
|
||||
}
|
||||
|
||||
err := editTierCredentials(ctx, adminClient, params)
|
||||
|
||||
assert.Equal(nil, err, fmt.Sprintf("Failed on %s: Error returned", function))
|
||||
|
||||
// Test-2: editTierCredentials() update Tier configuration failure
|
||||
minioEditTiersMock = func(_ context.Context, _ string, _ madmin.TierCreds) error {
|
||||
return errors.New("error message")
|
||||
}
|
||||
|
||||
errT2 := editTierCredentials(ctx, adminClient, params)
|
||||
|
||||
assert.Equal(errors.New("error message"), errT2, fmt.Sprintf("Failed on %s: Error returned", function))
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/websocket"
|
||||
)
|
||||
|
||||
// shortTraceMsg Short trace record
|
||||
type shortTraceMsg struct {
|
||||
Host string `json:"host"`
|
||||
Time string `json:"time"`
|
||||
Client string `json:"client"`
|
||||
CallStats callStats `json:"callStats"`
|
||||
FuncName string `json:"api"`
|
||||
Path string `json:"path"`
|
||||
Query string `json:"query"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
StatusMsg string `json:"statusMsg"`
|
||||
}
|
||||
|
||||
type callStats struct {
|
||||
Rx int `json:"rx"`
|
||||
Tx int `json:"tx"`
|
||||
Duration string `json:"duration"`
|
||||
Ttfb string `json:"timeToFirstByte"`
|
||||
}
|
||||
|
||||
// trace filters
|
||||
func matchTrace(opts TraceRequest, traceInfo madmin.ServiceTraceInfo) bool {
|
||||
statusCode := int(opts.statusCode)
|
||||
method := opts.method
|
||||
funcName := opts.funcName
|
||||
apiPath := opts.path
|
||||
|
||||
if statusCode == 0 && method == "" && funcName == "" && apiPath == "" {
|
||||
// no specific filtering found trace all the requests
|
||||
return true
|
||||
}
|
||||
|
||||
// Filter request path if passed by the user
|
||||
if apiPath != "" {
|
||||
pathToLookup := strings.ToLower(apiPath)
|
||||
pathFromTrace := strings.ToLower(traceInfo.Trace.Path)
|
||||
|
||||
return strings.Contains(pathFromTrace, pathToLookup)
|
||||
}
|
||||
|
||||
// Filter response status codes if passed by the user
|
||||
if statusCode > 0 && traceInfo.Trace.HTTP != nil {
|
||||
statusCodeFromTrace := traceInfo.Trace.HTTP.RespInfo.StatusCode
|
||||
|
||||
return statusCodeFromTrace == statusCode
|
||||
}
|
||||
|
||||
// Filter request method if passed by the user
|
||||
if method != "" && traceInfo.Trace.HTTP != nil {
|
||||
methodFromTrace := traceInfo.Trace.HTTP.ReqInfo.Method
|
||||
|
||||
return methodFromTrace == method
|
||||
}
|
||||
|
||||
if funcName != "" {
|
||||
funcToLookup := strings.ToLower(funcName)
|
||||
funcFromTrace := strings.ToLower(traceInfo.Trace.FuncName)
|
||||
|
||||
return strings.Contains(funcFromTrace, funcToLookup)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// startTraceInfo starts trace of the servers
|
||||
func startTraceInfo(ctx context.Context, conn WSConn, client MinioAdmin, opts TraceRequest) error {
|
||||
// Start listening on all trace activity.
|
||||
traceCh := client.serviceTrace(ctx, opts.threshold, opts.s3, opts.internal, opts.storage, opts.os, opts.onlyErrors)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case traceInfo, ok := <-traceCh:
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if traceInfo.Err != nil {
|
||||
LogError("error on serviceTrace: %v", traceInfo.Err)
|
||||
return traceInfo.Err
|
||||
}
|
||||
if matchTrace(opts, traceInfo) {
|
||||
// Serialize message to be sent
|
||||
traceInfoBytes, err := json.Marshal(shortTrace(&traceInfo))
|
||||
if err != nil {
|
||||
LogError("error on json.Marshal: %v", err)
|
||||
return err
|
||||
}
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, traceInfoBytes)
|
||||
if err != nil {
|
||||
LogError("error writeMessage: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shortTrace creates a shorter Trace Info message.
|
||||
// Same implementation as github/minio/mc/cmd/admin-trace.go
|
||||
func shortTrace(info *madmin.ServiceTraceInfo) shortTraceMsg {
|
||||
t := info.Trace
|
||||
s := shortTraceMsg{}
|
||||
|
||||
s.Time = t.Time.Format(time.RFC3339)
|
||||
s.Path = t.Path
|
||||
s.FuncName = t.FuncName
|
||||
s.CallStats.Duration = t.Duration.String()
|
||||
if info.Trace.HTTP != nil {
|
||||
s.Query = t.HTTP.ReqInfo.RawQuery
|
||||
s.StatusCode = t.HTTP.RespInfo.StatusCode
|
||||
s.StatusMsg = http.StatusText(t.HTTP.RespInfo.StatusCode)
|
||||
s.CallStats.Rx = t.HTTP.CallStats.InputBytes
|
||||
s.CallStats.Tx = t.HTTP.CallStats.OutputBytes
|
||||
s.CallStats.Ttfb = t.HTTP.CallStats.TimeToFirstByte.String()
|
||||
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]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAdminTrace(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
function := "startTraceInfo(ctx, )"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
testReceiver := make(chan shortTraceMsg, 5)
|
||||
textToReceive := "test"
|
||||
testStreamSize := 5
|
||||
isClosed := false // testReceiver is closed?
|
||||
|
||||
// Test-1: Serve Trace with no errors until trace finishes sending
|
||||
// define mock function behavior for minio server Trace
|
||||
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) {
|
||||
defer close(ch)
|
||||
lines := make([]int, testStreamSize)
|
||||
// mocking sending 5 lines of info
|
||||
for range lines {
|
||||
info := madmin.TraceInfo{
|
||||
FuncName: textToReceive,
|
||||
}
|
||||
ch <- madmin.ServiceTraceInfo{Trace: info}
|
||||
}
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
writesCount := 1
|
||||
// mock connection WriteMessage() no error
|
||||
connWriteMessageMock = func(_ int, data []byte) error {
|
||||
// emulate that receiver gets the message written
|
||||
var t shortTraceMsg
|
||||
_ = json.Unmarshal(data, &t)
|
||||
if writesCount == testStreamSize {
|
||||
// for testing we need to close the receiver channel
|
||||
if !isClosed {
|
||||
close(testReceiver)
|
||||
isClosed = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
testReceiver <- t
|
||||
writesCount++
|
||||
return nil
|
||||
}
|
||||
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{s3: true, internal: true, storage: true, os: true, onlyErrors: false}); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// check that the TestReceiver got the same number of data from trace.
|
||||
for i := range testReceiver {
|
||||
assert.Equal(textToReceive, i.FuncName)
|
||||
}
|
||||
|
||||
// Test-2: if error happens while writing, return error
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return fmt.Errorf("error on write")
|
||||
}
|
||||
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
|
||||
assert.Equal("error on write", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: error happens on serviceTrace Minio, trace should stop
|
||||
// and error shall be returned.
|
||||
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) {
|
||||
defer close(ch)
|
||||
lines := make([]int, 2)
|
||||
// mocking sending 5 lines of info
|
||||
for range lines {
|
||||
info := madmin.TraceInfo{
|
||||
NodeName: "test",
|
||||
}
|
||||
ch <- madmin.ServiceTraceInfo{Trace: info}
|
||||
}
|
||||
ch <- madmin.ServiceTraceInfo{Err: fmt.Errorf("error on trace")}
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
connWriteMessageMock = func(_ int, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
if err := startTraceInfo(ctx, mockWSConn, adminClient, TraceRequest{}); assert.Error(err) {
|
||||
assert.Equal("error on trace", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,742 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"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/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
)
|
||||
|
||||
// Policy evaluated constants
|
||||
const (
|
||||
Unknown = 0
|
||||
Allow = 1
|
||||
Deny = -1
|
||||
)
|
||||
|
||||
func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
// List Users
|
||||
api.UserListUsersHandler = userApi.ListUsersHandlerFunc(func(params userApi.ListUsersParams, session *models.Principal) middleware.Responder {
|
||||
listUsersResponse, err := getListUsersResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewListUsersDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewListUsersOK().WithPayload(listUsersResponse)
|
||||
})
|
||||
// Add User
|
||||
api.UserAddUserHandler = userApi.AddUserHandlerFunc(func(params userApi.AddUserParams, session *models.Principal) middleware.Responder {
|
||||
userResponse, err := getUserAddResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewAddUserDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewAddUserCreated().WithPayload(userResponse)
|
||||
})
|
||||
// Remove User
|
||||
api.UserRemoveUserHandler = userApi.RemoveUserHandlerFunc(func(params userApi.RemoveUserParams, session *models.Principal) middleware.Responder {
|
||||
err := getRemoveUserResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewRemoveUserDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewRemoveUserNoContent()
|
||||
})
|
||||
// Update User-Groups
|
||||
api.UserUpdateUserGroupsHandler = userApi.UpdateUserGroupsHandlerFunc(func(params userApi.UpdateUserGroupsParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewUpdateUserGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewUpdateUserGroupsOK().WithPayload(userUpdateResponse)
|
||||
})
|
||||
// Get User
|
||||
api.UserGetUserInfoHandler = userApi.GetUserInfoHandlerFunc(func(params userApi.GetUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userInfoResponse, err := getUserInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewGetUserInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewGetUserInfoOK().WithPayload(userInfoResponse)
|
||||
})
|
||||
// Update User
|
||||
api.UserUpdateUserInfoHandler = userApi.UpdateUserInfoHandlerFunc(func(params userApi.UpdateUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewUpdateUserInfoDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewUpdateUserInfoOK().WithPayload(userUpdateResponse)
|
||||
})
|
||||
// Update User-Groups Bulk
|
||||
api.UserBulkUpdateUsersGroupsHandler = userApi.BulkUpdateUsersGroupsHandlerFunc(func(params userApi.BulkUpdateUsersGroupsParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddUsersListToGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewBulkUpdateUsersGroupsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
|
||||
return userApi.NewBulkUpdateUsersGroupsOK()
|
||||
})
|
||||
api.BucketListUsersWithAccessToBucketHandler = bucketApi.ListUsersWithAccessToBucketHandlerFunc(func(params bucketApi.ListUsersWithAccessToBucketParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getListUsersWithAccessToBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListUsersWithAccessToBucketDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return bucketApi.NewListUsersWithAccessToBucketOK().WithPayload(response)
|
||||
})
|
||||
// Change User Password
|
||||
api.AccountChangeUserPasswordHandler = accountApi.ChangeUserPasswordHandlerFunc(func(params accountApi.ChangeUserPasswordParams, session *models.Principal) middleware.Responder {
|
||||
err := getChangeUserPasswordResponse(session, params)
|
||||
if err != nil {
|
||||
return accountApi.NewChangeUserPasswordDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return accountApi.NewChangeUserPasswordCreated()
|
||||
})
|
||||
// Check number of Service Accounts for listed users
|
||||
api.UserCheckUserServiceAccountsHandler = userApi.CheckUserServiceAccountsHandlerFunc(func(params userApi.CheckUserServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
userSAList, err := getCheckUserSAResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewCheckUserServiceAccountsDefault(err.Code).WithPayload(err.APIError)
|
||||
}
|
||||
return userApi.NewCheckUserServiceAccountsOK().WithPayload(userSAList)
|
||||
})
|
||||
}
|
||||
|
||||
func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
|
||||
// Get list of all users in the MinIO
|
||||
// This call requires explicit authentication, no anonymous requests are
|
||||
// allowed for listing users.
|
||||
userMap, err := client.listUsers(ctx)
|
||||
if err != nil {
|
||||
return []*models.User{}, err
|
||||
}
|
||||
|
||||
var users []*models.User
|
||||
for accessKey, user := range userMap {
|
||||
userElem := &models.User{
|
||||
AccessKey: accessKey,
|
||||
Status: string(user.Status),
|
||||
Policy: strings.Split(user.PolicyName, ","),
|
||||
MemberOf: user.MemberOf,
|
||||
}
|
||||
users = append(users, userElem)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// getListUsersResponse performs listUsers() and serializes it to the handler's output
|
||||
func getListUsersResponse(session *models.Principal, params userApi.ListUsersParams) (*models.ListUsersResponse, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listUsersResponse := &models.ListUsersResponse{
|
||||
Users: users,
|
||||
}
|
||||
return listUsersResponse, nil
|
||||
}
|
||||
|
||||
// addUser invokes adding a users on `MinioAdmin` and builds the response `models.User`
|
||||
func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *string, groups []string, policies []string) (*models.User, error) {
|
||||
// Calls into MinIO to add a new user if there's an errors return it
|
||||
if err := client.addUser(ctx, *accessKey, *secretKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// set groups for the newly created user
|
||||
var userWithGroups *models.User
|
||||
if len(groups) > 0 {
|
||||
var errUG error
|
||||
userWithGroups, errUG = updateUserGroups(ctx, client, *accessKey, groups)
|
||||
|
||||
if errUG != nil {
|
||||
return nil, errUG
|
||||
}
|
||||
}
|
||||
// set policies for the newly created user
|
||||
if len(policies) > 0 {
|
||||
policyString := strings.Join(policies, ",")
|
||||
if err := SetPolicy(ctx, client, policyString, *accessKey, "user"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
memberOf := []string{}
|
||||
status := "enabled"
|
||||
if userWithGroups != nil {
|
||||
memberOf = userWithGroups.MemberOf
|
||||
status = userWithGroups.Status
|
||||
}
|
||||
|
||||
userRet := &models.User{
|
||||
AccessKey: *accessKey,
|
||||
MemberOf: memberOf,
|
||||
Policy: policies,
|
||||
Status: status,
|
||||
}
|
||||
return userRet, nil
|
||||
}
|
||||
|
||||
func getUserAddResponse(session *models.Principal, params userApi.AddUserParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
var userExists bool
|
||||
|
||||
_, err = adminClient.getUserInfo(ctx, *params.Body.AccessKey)
|
||||
userExists = err == nil
|
||||
|
||||
if userExists {
|
||||
return nil, ErrorWithContext(ctx, ErrNonUniqueAccessKey)
|
||||
}
|
||||
user, err := addUser(
|
||||
ctx,
|
||||
adminClient,
|
||||
params.Body.AccessKey,
|
||||
params.Body.SecretKey,
|
||||
params.Body.Groups,
|
||||
params.Body.Policies,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// removeUser invokes removing an user on `MinioAdmin`, then we return the response from API
|
||||
func removeUser(ctx context.Context, client MinioAdmin, accessKey string) error {
|
||||
return client.removeUser(ctx, accessKey)
|
||||
}
|
||||
|
||||
func getRemoveUserResponse(session *models.Principal, params userApi.RemoveUserParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
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 {
|
||||
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 {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUserInfo calls MinIO server get the User Information
|
||||
func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*madmin.UserInfo, error) {
|
||||
userInfo, err := client.getUserInfo(ctx, accessKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a 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)
|
||||
}
|
||||
|
||||
user, err := getUserInfo(ctx, adminClient, userName)
|
||||
if err != nil {
|
||||
// User doesn't exist, return 404
|
||||
if madmin.ToErrorResponse(err).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode := 404
|
||||
errorMessage := "User doesn't exist"
|
||||
return nil, &CodedAPIError{Code: errorCode, APIError: &models.APIError{Message: errorMessage, DetailedMessage: err.Error()}}
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var policies []string
|
||||
if user.PolicyName == "" {
|
||||
policies = []string{}
|
||||
} else {
|
||||
policies = strings.Split(user.PolicyName, ",")
|
||||
}
|
||||
|
||||
hasPolicy := true
|
||||
|
||||
if len(policies) == 0 {
|
||||
hasPolicy = false
|
||||
for i := 0; i < len(user.MemberOf); i++ {
|
||||
group, err := adminClient.getGroupDescription(ctx, user.MemberOf[i])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if group.Policy != "" {
|
||||
hasPolicy = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userInformation := &models.User{
|
||||
AccessKey: userName,
|
||||
MemberOf: user.MemberOf,
|
||||
Policy: policies,
|
||||
Status: string(user.Status),
|
||||
HasPolicy: hasPolicy,
|
||||
}
|
||||
|
||||
return userInformation, nil
|
||||
}
|
||||
|
||||
// updateUserGroups invokes getUserInfo() to get the old groups from the user,
|
||||
// then we merge the list with the new groups list to have a shorter iteration between groups and we do a comparison between the current and old groups.
|
||||
// We delete or update the groups according the location in each list and send the user with the new groups from `MinioAdmin` to the client
|
||||
func updateUserGroups(ctx context.Context, client MinioAdmin, user string, groupsToAssign []string) (*models.User, error) {
|
||||
parallelUserUpdate := func(groupName string, originGroups []string) chan error {
|
||||
chProcess := make(chan error)
|
||||
|
||||
go func() error {
|
||||
defer close(chProcess)
|
||||
|
||||
// Compare if groupName is in the arrays
|
||||
isGroupPersistent := IsElementInArray(groupsToAssign, groupName)
|
||||
isInOriginGroups := IsElementInArray(originGroups, groupName)
|
||||
|
||||
if isGroupPersistent && isInOriginGroups { // Group is already assigned and doesn't need to be updated
|
||||
chProcess <- nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
isRemove := false // User is added by default
|
||||
|
||||
// User is deleted from the group
|
||||
if !isGroupPersistent {
|
||||
isRemove = true
|
||||
}
|
||||
|
||||
userToAddRemove := []string{user}
|
||||
|
||||
updateReturn := updateGroupMembers(ctx, client, groupName, userToAddRemove, isRemove)
|
||||
|
||||
chProcess <- updateReturn
|
||||
|
||||
return updateReturn
|
||||
}()
|
||||
|
||||
return chProcess
|
||||
}
|
||||
|
||||
userInfoOr, err := getUserInfo(ctx, client, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memberOf := userInfoOr.MemberOf
|
||||
mergedGroupArray := UniqueKeys(append(memberOf, groupsToAssign...))
|
||||
|
||||
var listOfUpdates []chan error
|
||||
|
||||
// Each group must be updated individually because there is no way to update all the groups at once for a user,
|
||||
// we are using the same logic as 'mc admin group add' command
|
||||
for _, groupN := range mergedGroupArray {
|
||||
proc := parallelUserUpdate(groupN, memberOf)
|
||||
listOfUpdates = append(listOfUpdates, proc)
|
||||
}
|
||||
|
||||
channelHasError := false
|
||||
|
||||
for _, chanRet := range listOfUpdates {
|
||||
locError := <-chanRet
|
||||
|
||||
if locError != nil {
|
||||
channelHasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if channelHasError {
|
||||
errRt := errors.New(500, "there was an error updating the groups")
|
||||
return nil, errRt
|
||||
}
|
||||
|
||||
userInfo, err := getUserInfo(ctx, client, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policies := strings.Split(userInfo.PolicyName, ",")
|
||||
|
||||
userReturn := &models.User{
|
||||
AccessKey: user,
|
||||
MemberOf: userInfo.MemberOf,
|
||||
Policy: policies,
|
||||
Status: string(userInfo.Status),
|
||||
}
|
||||
|
||||
return userReturn, nil
|
||||
}
|
||||
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params userApi.UpdateUserGroupsParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a 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)
|
||||
}
|
||||
|
||||
user, err := updateUserGroups(ctx, adminClient, userName, params.Body.Groups)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// setUserStatus invokes setUserStatus from madmin to update user status
|
||||
func setUserStatus(ctx context.Context, client MinioAdmin, user string, status string) error {
|
||||
var setStatus madmin.AccountStatus
|
||||
switch status {
|
||||
case "enabled":
|
||||
setStatus = madmin.AccountEnabled
|
||||
case "disabled":
|
||||
setStatus = madmin.AccountDisabled
|
||||
default:
|
||||
return errors.New(500, "status not valid")
|
||||
}
|
||||
|
||||
return client.setUserStatus(ctx, user, setStatus)
|
||||
}
|
||||
|
||||
func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserInfoParams) (*models.User, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a 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 {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
userElem, errUG := updateUserGroups(ctx, adminClient, userName, groups)
|
||||
|
||||
if errUG != nil {
|
||||
return nil, ErrorWithContext(ctx, errUG)
|
||||
}
|
||||
return userElem, nil
|
||||
}
|
||||
|
||||
// addUsersListToGroups iterates over the user list & assigns the requested groups to each user.
|
||||
func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate []string, groupsToAssign []string) error {
|
||||
// We update each group with the complete usersList
|
||||
parallelGroupsUpdate := func(groupToAssign string) chan error {
|
||||
groupProcess := make(chan error)
|
||||
|
||||
go func() {
|
||||
defer close(groupProcess)
|
||||
// We add the users array to the group.
|
||||
err := updateGroupMembers(ctx, client, groupToAssign, usersToUpdate, false)
|
||||
|
||||
groupProcess <- err
|
||||
}()
|
||||
return groupProcess
|
||||
}
|
||||
|
||||
var groupsUpdateList []chan error
|
||||
|
||||
// We get each group name & add users accordingly
|
||||
for _, groupName := range groupsToAssign {
|
||||
// We update the group
|
||||
proc := parallelGroupsUpdate(groupName)
|
||||
groupsUpdateList = append(groupsUpdateList, proc)
|
||||
}
|
||||
|
||||
errorsList := []string{} // We get the errors list because we want to have all errors at once.
|
||||
for _, err := range groupsUpdateList {
|
||||
errorFromUpdate := <-err // We store the errors to avoid Data Race
|
||||
if errorFromUpdate != nil {
|
||||
// If there is an errors, we store the errors strings so we can join them after we receive all errors
|
||||
errorsList = append(errorsList, errorFromUpdate.Error()) // We wait until all the channels have been closed.
|
||||
}
|
||||
}
|
||||
|
||||
// If there are errors, we throw the final errors with the errors inside
|
||||
if len(errorsList) > 0 {
|
||||
errGen := fmt.Errorf("error in users-groups assignation: %q", strings.Join(errorsList, ","))
|
||||
return errGen
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.BulkUpdateUsersGroupsParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
usersList := params.Body.Users
|
||||
groupsList := params.Body.Groups
|
||||
|
||||
if err := addUsersListToGroups(ctx, adminClient, usersList, groupsList); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getListUsersWithAccessToBucketResponse(session *models.Principal, params bucketApi.ListUsersWithAccessToBucketParams) ([]string, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
list, err := listUsersWithAccessToBucket(ctx, adminClient, params.Bucket)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func policyAllowsAndMatchesBucket(policy *iampolicy.Policy, bucket string) int {
|
||||
policyStatements := policy.Statements
|
||||
for i := 0; i < len(policyStatements); i++ {
|
||||
resources := policyStatements[i].Resources
|
||||
effect := policyStatements[i].Effect
|
||||
if resources.Match(bucket, map[string][]string{}) {
|
||||
if effect.IsValid() {
|
||||
if effect.IsAllowed(true) {
|
||||
return Allow
|
||||
}
|
||||
return Deny
|
||||
}
|
||||
}
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
|
||||
func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bucket string) ([]string, error) {
|
||||
users, err := adminClient.listUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var retval []string
|
||||
akHasAccess := make(map[string]struct{})
|
||||
akIsDenied := make(map[string]struct{})
|
||||
for k, v := range users {
|
||||
for _, policyName := range strings.Split(v.PolicyName, ",") {
|
||||
policyName = strings.TrimSpace(policyName)
|
||||
if policyName == "" {
|
||||
continue
|
||||
}
|
||||
policy, err := adminClient.getPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch policy %s: %v", policyName, err))
|
||||
continue
|
||||
}
|
||||
if _, ok := akIsDenied[k]; !ok {
|
||||
switch policyAllowsAndMatchesBucket(policy, bucket) {
|
||||
case Allow:
|
||||
if _, ok := akHasAccess[k]; !ok {
|
||||
akHasAccess[k] = struct{}{}
|
||||
}
|
||||
case Deny:
|
||||
akIsDenied[k] = struct{}{}
|
||||
delete(akHasAccess, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to list groups: %v", err))
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
for _, groupName := range groups {
|
||||
info, err := groupInfo(ctx, adminClient, groupName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch group info %s: %v", groupName, err))
|
||||
continue
|
||||
}
|
||||
policy, err := adminClient.getPolicy(ctx, info.Policy)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch group policy %s: %v", info.Policy, err))
|
||||
continue
|
||||
}
|
||||
for _, member := range info.Members {
|
||||
if _, ok := akIsDenied[member]; !ok {
|
||||
switch policyAllowsAndMatchesBucket(policy, bucket) {
|
||||
case Allow:
|
||||
if _, ok := akHasAccess[member]; !ok {
|
||||
akHasAccess[member] = struct{}{}
|
||||
}
|
||||
case Deny:
|
||||
akIsDenied[member] = struct{}{}
|
||||
delete(akHasAccess, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for k := range akHasAccess {
|
||||
retval = append(retval, k)
|
||||
}
|
||||
sort.Strings(retval)
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// changeUserPassword changes password of selectedUser to newSecretKey
|
||||
func changeUserPassword(ctx context.Context, client MinioAdmin, selectedUser string, newSecretKey string) error {
|
||||
return client.changePassword(ctx, selectedUser, newSecretKey)
|
||||
}
|
||||
|
||||
// getChangeUserPasswordResponse will change the password of selctedUser to newSecretKey
|
||||
func getChangeUserPasswordResponse(session *models.Principal, params accountApi.ChangeUserPasswordParams) *CodedAPIError {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
// params will contain selectedUser and newSecretKey credentials for the user
|
||||
user := *params.Body.SelectedUser
|
||||
newSecretKey := *params.Body.NewSecretKey
|
||||
|
||||
// changes password of user to newSecretKey
|
||||
if err := changeUserPassword(ctx, adminClient, user, newSecretKey); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCheckUserSAResponse(session *models.Principal, params userApi.CheckUserServiceAccountsParams) (*models.UserServiceAccountSummary, *CodedAPIError) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
var userServiceAccountList []*models.UserServiceAccountItem
|
||||
hasSA := false
|
||||
for _, user := range params.SelectedUsers {
|
||||
listServAccs, err := adminClient.listServiceAccounts(ctx, user)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
numSAs := int64(len(listServAccs.Accounts))
|
||||
if numSAs > 0 {
|
||||
hasSA = true
|
||||
}
|
||||
userAccountItem := &models.UserServiceAccountItem{
|
||||
UserName: user,
|
||||
NumSAs: numSAs,
|
||||
}
|
||||
userServiceAccountList = append(userServiceAccountList, userAccountItem)
|
||||
}
|
||||
|
||||
userAccountList := &models.UserServiceAccountSummary{
|
||||
UserServiceAccountList: userServiceAccountList,
|
||||
HasSA: hasSA,
|
||||
}
|
||||
|
||||
return userAccountList, nil
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/madmin-go/v3"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestListUsers(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1 : listUsers() Get response from minio client with two users and return the same number on listUsers()
|
||||
// mock minIO client
|
||||
mockUserMap := map[string]madmin.UserInfo{
|
||||
"ABCDEFGHI": {
|
||||
SecretKey: "",
|
||||
PolicyName: "ABCDEFGHI-policy",
|
||||
Status: "enabled",
|
||||
MemberOf: []string{"group1", "group2"},
|
||||
},
|
||||
"ZBCDEFGHI": {
|
||||
SecretKey: "",
|
||||
PolicyName: "ZBCDEFGHI-policy",
|
||||
Status: "enabled",
|
||||
MemberOf: []string{"group1", "group2"},
|
||||
},
|
||||
}
|
||||
|
||||
// mock function response from listUsersWithContext(ctx)
|
||||
minioListUsersMock = func() (map[string]madmin.UserInfo, error) {
|
||||
return mockUserMap, nil
|
||||
}
|
||||
|
||||
// get list users response this response should have Name, CreationDate, Size and Access
|
||||
// as part of of each user
|
||||
function := "listUsers()"
|
||||
userMap, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// verify length of users is correct
|
||||
assert.Equal(len(mockUserMap), len(userMap), fmt.Sprintf("Failed on %s: length of user's lists is not the same", function))
|
||||
|
||||
for _, b := range userMap {
|
||||
assert.Contains(mockUserMap, b.AccessKey)
|
||||
assert.Equal(string(mockUserMap[b.AccessKey].Status), b.Status)
|
||||
assert.Equal(mockUserMap[b.AccessKey].PolicyName, strings.Join(b.Policy, ","))
|
||||
assert.ElementsMatch(mockUserMap[b.AccessKey].MemberOf, []string{"group1", "group2"})
|
||||
}
|
||||
|
||||
// Test-2 : listUsers() Return and see that the error is handled correctly and returned
|
||||
minioListUsersMock = func() (map[string]madmin.UserInfo, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
_, err = listUsers(ctx, adminClient)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUser(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Test-1: valid case of adding a user with a proper access key
|
||||
accessKey := "ABCDEFGHI"
|
||||
secretKey := "ABCDEFGHIABCDEFGHI"
|
||||
groups := []string{"group1", "group2", "group3"}
|
||||
policies := []string{}
|
||||
emptyGroupTest := []string{}
|
||||
mockResponse := &madmin.UserInfo{
|
||||
MemberOf: []string{"group1", "group2", "gropup3"},
|
||||
PolicyName: "",
|
||||
Status: "enabled",
|
||||
SecretKey: "",
|
||||
}
|
||||
|
||||
// mock function response from addUser() return no error
|
||||
minioAddUserMock = func(_, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
// Test-1: Add a user
|
||||
function := "addUser()"
|
||||
user, err := addUser(ctx, adminClient, &accessKey, &secretKey, groups, policies)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// no error should have been returned
|
||||
assert.Nil(err, "Error is not null")
|
||||
// the same access key should be in the model users
|
||||
assert.Equal(user.AccessKey, accessKey)
|
||||
|
||||
// Test-2 Add a user with empty groups list
|
||||
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, emptyGroupTest, policies)
|
||||
// no error should have been returned
|
||||
assert.Nil(err, "Error is not null")
|
||||
// the same access key should be in the model users
|
||||
assert.Equal(user.AccessKey, accessKey)
|
||||
|
||||
// Test-3: valid case
|
||||
accessKey = "AB"
|
||||
secretKey = "ABCDEFGHIABCDEFGHI"
|
||||
// mock function response from addUser() return no error
|
||||
minioAddUserMock = func(_, _ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, groups, policies)
|
||||
|
||||
// no error should have been returned
|
||||
assert.Nil(user, "User is not null")
|
||||
assert.NotNil(err, "An error should have been returned")
|
||||
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-4: add groups function returns an error
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, groups, policies)
|
||||
|
||||
// no error should have been returned
|
||||
assert.Nil(user, "User is not null")
|
||||
assert.NotNil(err, "An error should have been returned")
|
||||
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveUser(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
function := "removeUser()"
|
||||
|
||||
// Test-1: removeUser() delete a user
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioRemoveUserMock = func(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := removeUser(ctx, adminClient, "ABCDEFGHI"); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: removeUser() make sure errors are handled correctly when error on DeleteUser()
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioRemoveUserMock = func(_ string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
if err := removeUser(ctx, adminClient, "notexistentuser"); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserGroups(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
function := "updateUserGroups()"
|
||||
mockUserGroups := []string{"group1", "group2", "group3"}
|
||||
mockUserName := "testUser"
|
||||
mockResponse := &madmin.UserInfo{
|
||||
MemberOf: []string{"group1", "group2", "gropup3"},
|
||||
PolicyName: "",
|
||||
Status: "enabled",
|
||||
SecretKey: mockUserName,
|
||||
}
|
||||
mockEmptyResponse := &madmin.UserInfo{
|
||||
MemberOf: nil,
|
||||
PolicyName: "",
|
||||
Status: "",
|
||||
SecretKey: "",
|
||||
}
|
||||
|
||||
// Test-1: updateUserGroups() updates the groups for a user
|
||||
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
||||
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: updateUserGroups() make sure errors are handled correctly when error on UpdateGroupMembersMock()
|
||||
// mock function response from removeUser(accessKey)
|
||||
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); assert.Error(err) {
|
||||
assert.Equal("there was an error updating the groups", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: updateUserGroups() make sure we return the correct error when getUserInfo returns error
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockEmptyResponse, errors.New("error getting user ")
|
||||
}
|
||||
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); assert.Error(err) {
|
||||
assert.Equal("error getting user ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserInfo(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Test-1 : getUserInfo() get user info
|
||||
userName := "userNameTest"
|
||||
mockResponse := &madmin.UserInfo{
|
||||
SecretKey: userName,
|
||||
PolicyName: "",
|
||||
MemberOf: []string{"group1", "group2", "group3"},
|
||||
Status: "enabled",
|
||||
}
|
||||
emptyMockResponse := &madmin.UserInfo{
|
||||
SecretKey: "",
|
||||
PolicyName: "",
|
||||
Status: "",
|
||||
MemberOf: nil,
|
||||
}
|
||||
|
||||
// mock function response from getUserInfo()
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *mockResponse, nil
|
||||
}
|
||||
function := "getUserInfo()"
|
||||
info, err := getUserInfo(ctx, adminClient, userName)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
assert.Equal(userName, info.SecretKey)
|
||||
assert.Equal("", info.PolicyName)
|
||||
assert.ElementsMatch([]string{"group1", "group2", "group3"}, info.MemberOf)
|
||||
assert.Equal(mockResponse.Status, info.Status)
|
||||
|
||||
// Test-2 : getUserInfo() Return error and see that the error is handled correctly and returned
|
||||
minioGetUserInfoMock = func(_ string) (madmin.UserInfo, error) {
|
||||
return *emptyMockResponse, errors.New("error")
|
||||
}
|
||||
_, err = getUserInfo(ctx, adminClient, userName)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUserStatus(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
adminClient := AdminClientMock{}
|
||||
function := "setUserStatus()"
|
||||
userName := "userName123"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Test-1: setUserStatus() update valid disabled status
|
||||
expectedStatus := "disabled"
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-2: setUserStatus() update valid enabled status
|
||||
expectedStatus = "enabled"
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
// Test-3: setUserStatus() update invalid status, should send error
|
||||
expectedStatus = "invalid"
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return nil
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
||||
assert.Equal("status not valid", err.Error())
|
||||
}
|
||||
// Test-4: setUserStatus() handler error correctly
|
||||
expectedStatus = "enabled"
|
||||
minioSetUserStatusMock = func(_ string, _ madmin.AccountStatus) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserGroupsBulk(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
// mock minIO client
|
||||
adminClient := AdminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
function := "updateUserGroups()"
|
||||
mockUserGroups := []string{"group1", "group2", "group3"}
|
||||
mockUsers := []string{"testUser", "testUser2"}
|
||||
|
||||
// Test-1: addUsersListToGroups() updates the groups for a users list
|
||||
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: addUsersListToGroups() make sure errors are handled correctly when error on updateGroupMembers()
|
||||
// mock function response from removeUser(accessKey)
|
||||
minioUpdateGroupMembersMock = func(_ madmin.GroupAddRemove) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); assert.Error(err) {
|
||||
assert.Equal("error in users-groups assignation: \"error,error,error\"", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestListUsersWithAccessToBucket(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
adminClient := AdminClientMock{}
|
||||
user1 := madmin.UserInfo{
|
||||
SecretKey: "testtest",
|
||||
PolicyName: "consoleAdmin,testPolicy,redundantPolicy",
|
||||
Status: "enabled",
|
||||
MemberOf: []string{"group1"},
|
||||
}
|
||||
user2 := madmin.UserInfo{
|
||||
SecretKey: "testtest",
|
||||
PolicyName: "testPolicy, otherPolicy",
|
||||
Status: "enabled",
|
||||
MemberOf: []string{"group1"},
|
||||
}
|
||||
mockUsers := map[string]madmin.UserInfo{"testuser1": user1, "testuser2": user2}
|
||||
minioListUsersMock = func() (map[string]madmin.UserInfo, error) {
|
||||
return mockUsers, nil
|
||||
}
|
||||
policyMap := map[string]string{
|
||||
"consoleAdmin": `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"admin:*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
"testPolicy": `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Deny",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
"otherPolicy": `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
"thirdPolicy": `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket3"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`, "RedundantPolicy": `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
}
|
||||
minioGetPolicyMock = func(name string) (*iampolicy.Policy, error) {
|
||||
iamp, err := iampolicy.ParseConfig(bytes.NewReader([]byte(policyMap[name])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iamp, nil
|
||||
}
|
||||
minioListGroupsMock = func() ([]string, error) {
|
||||
return []string{"group1"}, nil
|
||||
}
|
||||
minioGetGroupDescriptionMock = func(name string) (*madmin.GroupDesc, error) {
|
||||
if name == "group1" {
|
||||
mockResponse := &madmin.GroupDesc{
|
||||
Name: "group1",
|
||||
Policy: "thirdPolicy",
|
||||
Members: []string{"testuser1", "testuser2"},
|
||||
Status: "enabled",
|
||||
}
|
||||
return mockResponse, nil
|
||||
}
|
||||
return nil, ErrDefault
|
||||
}
|
||||
type args struct {
|
||||
bucket string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "Test1",
|
||||
args: args{bucket: "bucket0"},
|
||||
want: []string{"testuser1"},
|
||||
},
|
||||
{
|
||||
name: "Test2",
|
||||
args: args{bucket: "bucket1"},
|
||||
want: []string(nil),
|
||||
},
|
||||
{
|
||||
name: "Test3",
|
||||
args: args{bucket: "bucket2"},
|
||||
want: []string{"testuser1", "testuser2"},
|
||||
},
|
||||
{
|
||||
name: "Test4",
|
||||
args: args{bucket: "bucket3"},
|
||||
want: []string{"testuser1", "testuser2"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
got, _ := listUsersWithAccessToBucket(ctx, adminClient, tt.args.bucket)
|
||||
assert.Equal(got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,12 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg"
|
||||
|
||||
@@ -35,7 +31,6 @@ import (
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go/v3"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
iampolicy "github.com/minio/pkg/v2/policy"
|
||||
)
|
||||
|
||||
const globalAppName = "MinIO Console"
|
||||
@@ -44,98 +39,9 @@ const globalAppName = "MinIO Console"
|
||||
// by mock when testing, it should include all MinioAdmin respective api calls
|
||||
// that are used within this project.
|
||||
type MinioAdmin interface {
|
||||
listUsers(ctx context.Context) (map[string]madmin.UserInfo, error)
|
||||
addUser(ctx context.Context, acessKey, SecretKey string) error
|
||||
removeUser(ctx context.Context, accessKey string) error
|
||||
getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error)
|
||||
setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error
|
||||
listGroups(ctx context.Context) ([]string, error)
|
||||
updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error
|
||||
getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error)
|
||||
setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error
|
||||
listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error)
|
||||
getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error)
|
||||
removePolicy(ctx context.Context, name string) error
|
||||
addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error
|
||||
setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error
|
||||
getConfigKV(ctx context.Context, key string) ([]byte, error)
|
||||
helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error)
|
||||
helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error)
|
||||
setConfigKV(ctx context.Context, kv string) (restart bool, err error)
|
||||
delConfigKV(ctx context.Context, kv string) (err error)
|
||||
serviceRestart(ctx context.Context) error
|
||||
serverInfo(ctx context.Context) (madmin.InfoMessage, error)
|
||||
startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error)
|
||||
stopProfiling(ctx context.Context) (io.ReadCloser, error)
|
||||
serviceTrace(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
AccountInfo(ctx context.Context) (madmin.AccountInfo, error)
|
||||
heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error)
|
||||
// Service Accounts
|
||||
addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error)
|
||||
listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error)
|
||||
deleteServiceAccount(ctx context.Context, serviceAccount string) error
|
||||
infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error)
|
||||
updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error
|
||||
// Remote Buckets
|
||||
listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error)
|
||||
getRemoteBucket(ctx context.Context, bucket, arnType string) (targets *madmin.BucketTarget, err error)
|
||||
removeRemoteBucket(ctx context.Context, bucket, arn string) error
|
||||
addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error)
|
||||
// Account password management
|
||||
changePassword(ctx context.Context, accessKey, secretKey string) error
|
||||
serverHealthInfo(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error)
|
||||
// List Tiers
|
||||
listTiers(ctx context.Context) ([]*madmin.TierConfig, error)
|
||||
// Tier Info
|
||||
tierStats(ctx context.Context) ([]madmin.TierInfo, error)
|
||||
// Add Tier
|
||||
addTier(ctx context.Context, tier *madmin.TierConfig) error
|
||||
// Edit Tier Credentials
|
||||
editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error
|
||||
// verify Tier status
|
||||
verifyTierStatus(ctx context.Context, tierName string) error
|
||||
// Speedtest
|
||||
speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error)
|
||||
// Site Relication
|
||||
getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error)
|
||||
addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error)
|
||||
editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error)
|
||||
deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error)
|
||||
|
||||
// Replication status
|
||||
getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error)
|
||||
|
||||
// KMS
|
||||
kmsStatus(ctx context.Context) (madmin.KMSStatus, error)
|
||||
kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error)
|
||||
kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error)
|
||||
kmsVersion(ctx context.Context) (*madmin.KMSVersion, error)
|
||||
createKey(ctx context.Context, key string) error
|
||||
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)
|
||||
listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error)
|
||||
deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error)
|
||||
getIDPConfig(ctx context.Context, cfgType, cfgName string) (c madmin.IDPConfig, err error)
|
||||
|
||||
// LDAP
|
||||
getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error)
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
@@ -146,305 +52,17 @@ type AdminClient struct {
|
||||
Client *madmin.AdminClient
|
||||
}
|
||||
|
||||
func (ac AdminClient) changePassword(ctx context.Context, accessKey, secretKey string) error {
|
||||
return ac.Client.SetUser(ctx, accessKey, secretKey, madmin.AccountEnabled)
|
||||
}
|
||||
|
||||
// implements madmin.ListUsers()
|
||||
func (ac AdminClient) listUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
||||
return ac.Client.ListUsers(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.AddUser()
|
||||
func (ac AdminClient) addUser(ctx context.Context, accessKey, secretKey string) error {
|
||||
return ac.Client.AddUser(ctx, accessKey, secretKey)
|
||||
}
|
||||
|
||||
// implements madmin.RemoveUser()
|
||||
func (ac AdminClient) removeUser(ctx context.Context, accessKey string) error {
|
||||
return ac.Client.RemoveUser(ctx, accessKey)
|
||||
}
|
||||
|
||||
// implements madmin.GetUserInfo()
|
||||
func (ac AdminClient) getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error) {
|
||||
return ac.Client.GetUserInfo(ctx, accessKey)
|
||||
}
|
||||
|
||||
// implements madmin.SetUserStatus()
|
||||
func (ac AdminClient) setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error {
|
||||
return ac.Client.SetUserStatus(ctx, accessKey, status)
|
||||
}
|
||||
|
||||
// implements madmin.ListGroups()
|
||||
func (ac AdminClient) listGroups(ctx context.Context) ([]string, error) {
|
||||
return ac.Client.ListGroups(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.UpdateGroupMembers()
|
||||
func (ac AdminClient) updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error {
|
||||
return ac.Client.UpdateGroupMembers(ctx, greq)
|
||||
}
|
||||
|
||||
// implements madmin.GetGroupDescription(group)
|
||||
func (ac AdminClient) getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error) {
|
||||
return ac.Client.GetGroupDescription(ctx, group)
|
||||
}
|
||||
|
||||
// implements madmin.SetGroupStatus(group, status)
|
||||
func (ac AdminClient) setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error {
|
||||
return ac.Client.SetGroupStatus(ctx, group, status)
|
||||
}
|
||||
|
||||
// implements madmin.ListCannedPolicies()
|
||||
func (ac AdminClient) listPolicies(ctx context.Context) (map[string]*iampolicy.Policy, error) {
|
||||
policyMap, err := ac.Client.ListCannedPolicies(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies := make(map[string]*iampolicy.Policy, len(policyMap))
|
||||
for k, v := range policyMap {
|
||||
p, err := iampolicy.ParseConfig(bytes.NewReader(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policies[k] = p
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// implements madmin.ListCannedPolicies()
|
||||
func (ac AdminClient) getPolicy(ctx context.Context, name string) (*iampolicy.Policy, error) {
|
||||
info, err := ac.Client.InfoCannedPolicyV2(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iampolicy.ParseConfig(bytes.NewReader(info.Policy))
|
||||
}
|
||||
|
||||
// implements madmin.RemoveCannedPolicy()
|
||||
func (ac AdminClient) removePolicy(ctx context.Context, name string) error {
|
||||
return ac.Client.RemoveCannedPolicy(ctx, name)
|
||||
}
|
||||
|
||||
// implements madmin.AddCannedPolicy()
|
||||
func (ac AdminClient) addPolicy(ctx context.Context, name string, policy *iampolicy.Policy) error {
|
||||
buf, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ac.Client.AddCannedPolicy(ctx, name, buf)
|
||||
}
|
||||
|
||||
// implements madmin.SetPolicy()
|
||||
func (ac AdminClient) setPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error {
|
||||
// nolint:staticcheck // ignore SA1019
|
||||
return ac.Client.SetPolicy(ctx, policyName, entityName, isGroup)
|
||||
}
|
||||
|
||||
// implements madmin.GetConfigKV()
|
||||
func (ac AdminClient) getConfigKV(ctx context.Context, key string) ([]byte, error) {
|
||||
return ac.Client.GetConfigKV(ctx, key)
|
||||
}
|
||||
|
||||
// implements madmin.HelpConfigKV()
|
||||
func (ac AdminClient) helpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return ac.Client.HelpConfigKV(ctx, subSys, key, envOnly)
|
||||
}
|
||||
|
||||
// implements madmin.helpConfigKVGlobal()
|
||||
func (ac AdminClient) helpConfigKVGlobal(ctx context.Context, envOnly bool) (madmin.Help, error) {
|
||||
return ac.Client.HelpConfigKV(ctx, "", "", envOnly)
|
||||
}
|
||||
|
||||
// implements madmin.SetConfigKV()
|
||||
func (ac AdminClient) setConfigKV(ctx context.Context, kv string) (restart bool, err error) {
|
||||
return ac.Client.SetConfigKV(ctx, kv)
|
||||
}
|
||||
|
||||
// implements madmin.DelConfigKV()
|
||||
func (ac AdminClient) delConfigKV(ctx context.Context, kv string) (err error) {
|
||||
_, err = ac.Client.DelConfigKV(ctx, kv)
|
||||
return err
|
||||
}
|
||||
|
||||
// implements madmin.ServiceRestart()
|
||||
func (ac AdminClient) serviceRestart(ctx context.Context) (err error) {
|
||||
return ac.Client.ServiceRestart(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.ServerInfo()
|
||||
func (ac AdminClient) serverInfo(ctx context.Context) (madmin.InfoMessage, error) {
|
||||
return ac.Client.ServerInfo(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.StartProfiling()
|
||||
func (ac AdminClient) startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
|
||||
return ac.Client.StartProfiling(ctx, profiler)
|
||||
}
|
||||
|
||||
// implements madmin.DownloadProfilingData()
|
||||
func (ac AdminClient) stopProfiling(ctx context.Context) (io.ReadCloser, error) {
|
||||
return ac.Client.DownloadProfilingData(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.ServiceTrace()
|
||||
func (ac AdminClient) serviceTrace(ctx context.Context, threshold int64, _, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo {
|
||||
thresholdT := time.Duration(threshold)
|
||||
|
||||
tracingOptions := madmin.ServiceTraceOpts{
|
||||
S3: true,
|
||||
OnlyErrors: errTrace,
|
||||
Internal: internal,
|
||||
Storage: storage,
|
||||
OS: os,
|
||||
Threshold: thresholdT,
|
||||
}
|
||||
|
||||
return ac.Client.ServiceTrace(ctx, tracingOptions)
|
||||
}
|
||||
|
||||
// implements madmin.GetLogs()
|
||||
func (ac AdminClient) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return ac.Client.GetLogs(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
// implements madmin.AddServiceAccount()
|
||||
func (ac AdminClient) addServiceAccount(ctx context.Context, policy string, user string, accessKey string, secretKey string, name string, description string, expiry *time.Time, comment string) (madmin.Credentials, error) {
|
||||
return ac.Client.AddServiceAccount(ctx, madmin.AddServiceAccountReq{
|
||||
Policy: []byte(policy),
|
||||
TargetUser: user,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Expiration: expiry,
|
||||
Comment: comment,
|
||||
})
|
||||
}
|
||||
|
||||
// implements madmin.ListServiceAccounts()
|
||||
func (ac AdminClient) listServiceAccounts(ctx context.Context, user string) (madmin.ListServiceAccountsResp, error) {
|
||||
return ac.Client.ListServiceAccounts(ctx, user)
|
||||
}
|
||||
|
||||
// implements madmin.DeleteServiceAccount()
|
||||
func (ac AdminClient) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return ac.Client.DeleteServiceAccount(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
// implements madmin.InfoServiceAccount()
|
||||
func (ac AdminClient) infoServiceAccount(ctx context.Context, serviceAccount string) (madmin.InfoServiceAccountResp, error) {
|
||||
return ac.Client.InfoServiceAccount(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
// implements madmin.UpdateServiceAccount()
|
||||
func (ac AdminClient) updateServiceAccount(ctx context.Context, serviceAccount string, opts madmin.UpdateServiceAccountReq) error {
|
||||
return ac.Client.UpdateServiceAccount(ctx, serviceAccount, opts)
|
||||
}
|
||||
|
||||
// AccountInfo implements madmin.AccountInfo()
|
||||
func (ac AdminClient) AccountInfo(ctx context.Context) (madmin.AccountInfo, error) {
|
||||
return ac.Client.AccountInfo(ctx, madmin.AccountOpts{})
|
||||
}
|
||||
|
||||
func (ac AdminClient) heal(ctx context.Context, bucket, prefix string, healOpts madmin.HealOpts, clientToken string,
|
||||
forceStart, forceStop bool,
|
||||
) (healStart madmin.HealStartSuccess, healTaskStatus madmin.HealTaskStatus, err error) {
|
||||
return ac.Client.Heal(ctx, bucket, prefix, healOpts, clientToken, forceStart, forceStop)
|
||||
}
|
||||
|
||||
// listRemoteBuckets - return a list of remote buckets
|
||||
func (ac AdminClient) listRemoteBuckets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget, err error) {
|
||||
return ac.Client.ListRemoteTargets(ctx, bucket, arnType)
|
||||
}
|
||||
|
||||
// getRemoteBucket - gets remote bucked based on a given bucket name
|
||||
func (ac AdminClient) getRemoteBucket(ctx context.Context, bucket, arnType string) (*madmin.BucketTarget, error) {
|
||||
targets, err := ac.Client.ListRemoteTargets(ctx, bucket, arnType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(targets) > 0 {
|
||||
return &targets[0], nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// removeRemoteBucket removes a remote target associated with particular ARN for this bucket
|
||||
func (ac AdminClient) removeRemoteBucket(ctx context.Context, bucket, arn string) error {
|
||||
return ac.Client.RemoveRemoteTarget(ctx, bucket, arn)
|
||||
}
|
||||
|
||||
// addRemoteBucket sets up a remote target for this bucket
|
||||
func (ac AdminClient) addRemoteBucket(ctx context.Context, bucket string, target *madmin.BucketTarget) (string, error) {
|
||||
return ac.Client.SetRemoteTarget(ctx, bucket, target)
|
||||
}
|
||||
|
||||
func (ac AdminClient) setBucketQuota(ctx context.Context, bucket string, quota *madmin.BucketQuota) error {
|
||||
return ac.Client.SetBucketQuota(ctx, bucket, quota)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getBucketQuota(ctx context.Context, bucket string) (madmin.BucketQuota, error) {
|
||||
return ac.Client.GetBucketQuota(ctx, bucket)
|
||||
}
|
||||
|
||||
// serverHealthInfo implements mc.ServerHealthInfo - Connect to a minio server and call Health Info Management API
|
||||
func (ac AdminClient) serverHealthInfo(ctx context.Context, healthDataTypes []madmin.HealthDataType, deadline time.Duration) (interface{}, string, error) {
|
||||
info := madmin.HealthInfo{}
|
||||
var healthInfo interface{}
|
||||
var version string
|
||||
var tryCount int
|
||||
for info.Version == "" && tryCount < 10 {
|
||||
resp, version, err := ac.Client.ServerHealthInfo(ctx, healthDataTypes, deadline, "")
|
||||
if err != nil {
|
||||
return nil, version, err
|
||||
}
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
for {
|
||||
if err = decoder.Decode(&info); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
tryCount++
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
}
|
||||
if info.Version == "" {
|
||||
return nil, "", ErrHealthReportFail
|
||||
}
|
||||
healthInfo = info
|
||||
|
||||
return healthInfo, version, nil
|
||||
}
|
||||
|
||||
// implements madmin.listTiers()
|
||||
func (ac AdminClient) listTiers(ctx context.Context) ([]*madmin.TierConfig, error) {
|
||||
return ac.Client.ListTiers(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.tierStats()
|
||||
func (ac AdminClient) tierStats(ctx context.Context) ([]madmin.TierInfo, error) {
|
||||
return ac.Client.TierStats(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.AddTier()
|
||||
func (ac AdminClient) addTier(ctx context.Context, cfg *madmin.TierConfig) error {
|
||||
return ac.Client.AddTier(ctx, cfg)
|
||||
}
|
||||
|
||||
// implements madmin.Inspect()
|
||||
func (ac AdminClient) inspect(ctx context.Context, insOpts madmin.InspectOptions) ([]byte, io.ReadCloser, error) {
|
||||
return ac.Client.Inspect(ctx, insOpts)
|
||||
}
|
||||
|
||||
// implements madmin.EditTier()
|
||||
func (ac AdminClient) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error {
|
||||
return ac.Client.EditTier(ctx, tierName, creds)
|
||||
}
|
||||
|
||||
// implements madmin.VerifyTier()
|
||||
func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) error {
|
||||
return ac.Client.VerifyTier(ctx, tierName)
|
||||
func (ac AdminClient) kmsStatus(ctx context.Context) (madmin.KMSStatus, error) {
|
||||
return ac.Client.KMSStatus(ctx)
|
||||
}
|
||||
|
||||
func NewMinioAdminClient(ctx context.Context, sessionClaims *models.Principal) (*madmin.AdminClient, error) {
|
||||
@@ -474,19 +92,6 @@ func newAdminFromClaims(claims *models.Principal, clientIP string) (*madmin.Admi
|
||||
return adminClient, nil
|
||||
}
|
||||
|
||||
// newAdminFromCreds Creates a minio client using custom credentials for connecting to a remote host
|
||||
func newAdminFromCreds(accessKey, secretKey, endpoint string, tlsEnabled bool) (*madmin.AdminClient, error) {
|
||||
minioClient, err := madmin.NewWithOptions(endpoint, &madmin.Options{
|
||||
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
|
||||
Secure: tlsEnabled,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
minioClient.SetAppInfo(globalAppName, pkg.Version)
|
||||
return minioClient, nil
|
||||
}
|
||||
|
||||
// isLocalAddress returns true if the url contains an IPv4/IPv6 hostname
|
||||
// that points to the local machine - FQDN are not supported
|
||||
func isLocalIPEndpoint(endpoint string) bool {
|
||||
@@ -583,162 +188,3 @@ func getClientIP(r *http.Request) string {
|
||||
}
|
||||
return raddr
|
||||
}
|
||||
|
||||
func (ac AdminClient) speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) {
|
||||
return ac.Client.Speedtest(ctx, opts)
|
||||
}
|
||||
|
||||
// Site Replication
|
||||
func (ac AdminClient) getSiteReplicationInfo(ctx context.Context) (*madmin.SiteReplicationInfo, error) {
|
||||
res, err := ac.Client.SiteReplicationInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.SiteReplicationInfo{
|
||||
Enabled: res.Enabled,
|
||||
Name: res.Name,
|
||||
Sites: res.Sites,
|
||||
ServiceAccountAccessKey: res.ServiceAccountAccessKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) addSiteReplicationInfo(ctx context.Context, sites []madmin.PeerSite, opts madmin.SRAddOptions) (*madmin.ReplicateAddStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationAdd(ctx, sites, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &madmin.ReplicateAddStatus{
|
||||
Success: res.Success,
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
InitialSyncErrorMessage: res.InitialSyncErrorMessage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) editSiteReplicationInfo(ctx context.Context, site madmin.PeerInfo, opts madmin.SREditOptions) (*madmin.ReplicateEditStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationEdit(ctx, site, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.ReplicateEditStatus{
|
||||
Success: res.Success,
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteSiteReplicationInfo(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) {
|
||||
res, err := ac.Client.SiteReplicationRemove(ctx, removeReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &madmin.ReplicateRemoveStatus{
|
||||
Status: res.Status,
|
||||
ErrDetail: res.ErrDetail,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) getSiteReplicationStatus(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) {
|
||||
res, err := ac.Client.SRStatusInfo(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsStatus(ctx context.Context) (madmin.KMSStatus, error) {
|
||||
return ac.Client.KMSStatus(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsMetrics(ctx context.Context) (*madmin.KMSMetrics, error) {
|
||||
return ac.Client.KMSMetrics(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsAPIs(ctx context.Context) ([]madmin.KMSAPI, error) {
|
||||
return ac.Client.KMSAPIs(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) kmsVersion(ctx context.Context) (*madmin.KMSVersion, error) {
|
||||
return ac.Client.KMSVersion(ctx)
|
||||
}
|
||||
|
||||
func (ac AdminClient) createKey(ctx context.Context, key string) error {
|
||||
return ac.Client.CreateKey(ctx, key)
|
||||
}
|
||||
|
||||
func (ac AdminClient) 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)
|
||||
}
|
||||
|
||||
func (ac AdminClient) keyStatus(ctx context.Context, key string) (*madmin.KMSKeyStatus, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
func (ac AdminClient) listIDPConfig(ctx context.Context, idpType string) ([]madmin.IDPListItem, error) {
|
||||
return ac.Client.ListIDPConfig(ctx, idpType)
|
||||
}
|
||||
|
||||
func (ac AdminClient) deleteIDPConfig(ctx context.Context, idpType, cfgName string) (restart bool, err error) {
|
||||
return ac.Client.DeleteIDPConfig(ctx, idpType, cfgName)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getIDPConfig(ctx context.Context, idpType, cfgName string) (c madmin.IDPConfig, err error) {
|
||||
return ac.Client.GetIDPConfig(ctx, idpType, cfgName)
|
||||
}
|
||||
|
||||
func (ac AdminClient) getLDAPPolicyEntities(ctx context.Context, query madmin.PolicyEntitiesQuery) (madmin.PolicyEntitiesResult, error) {
|
||||
return ac.Client.GetLDAPPolicyEntities(ctx, query)
|
||||
}
|
||||
|
||||
110
api/client.go
110
api/client.go
@@ -21,13 +21,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/replication"
|
||||
"github.com/minio/minio-go/v7/pkg/sse"
|
||||
xnet "github.com/minio/pkg/v2/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
|
||||
@@ -140,11 +137,6 @@ func (c minioClient) getBucketVersioning(ctx context.Context, bucketName string)
|
||||
return c.client.GetBucketVersioning(ctx, bucketName)
|
||||
}
|
||||
|
||||
// implements minio.getBucketVersioning(ctx, bucketName)
|
||||
func (c minioClient) getBucketReplication(ctx context.Context, bucketName string) (replication.Config, error) {
|
||||
return c.client.GetBucketReplication(ctx, bucketName)
|
||||
}
|
||||
|
||||
// implements minio.listObjects(ctx)
|
||||
func (c minioClient) listObjects(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo {
|
||||
return c.client.ListObjects(ctx, bucket, opts)
|
||||
@@ -209,14 +201,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)
|
||||
}
|
||||
@@ -257,14 +241,6 @@ func (c mcClient) watch(ctx context.Context, options mc.WatchOptions) (*mc.Watch
|
||||
return c.client.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (c mcClient) setReplication(ctx context.Context, cfg *replication.Config, opts replication.Options) *probe.Error {
|
||||
return c.client.SetReplication(ctx, cfg, opts)
|
||||
}
|
||||
|
||||
func (c mcClient) deleteAllReplicationRules(ctx context.Context) *probe.Error {
|
||||
return c.client.RemoveReplication(ctx)
|
||||
}
|
||||
|
||||
func (c mcClient) setVersioning(ctx context.Context, status string, excludePrefix []string, excludeFolders bool) *probe.Error {
|
||||
return c.client.SetVersion(ctx, status, excludePrefix, excludeFolders)
|
||||
}
|
||||
@@ -299,6 +275,7 @@ type ConsoleCredentialsI interface {
|
||||
type ConsoleCredentials struct {
|
||||
ConsoleCredentials *credentials.Credentials
|
||||
AccountAccessKey string
|
||||
CredContext *credentials.CredContext
|
||||
}
|
||||
|
||||
func (c ConsoleCredentials) GetAccountAccessKey() string {
|
||||
@@ -307,7 +284,7 @@ func (c ConsoleCredentials) GetAccountAccessKey() string {
|
||||
|
||||
// Get implements *Login.Get()
|
||||
func (c ConsoleCredentials) Get() (credentials.Value, error) {
|
||||
return c.ConsoleCredentials.Get()
|
||||
return c.ConsoleCredentials.GetWithContext(c.CredContext)
|
||||
}
|
||||
|
||||
// Expire implements *Login.Expire()
|
||||
@@ -322,6 +299,10 @@ type consoleSTSAssumeRole struct {
|
||||
stsAssumeRole *credentials.STSAssumeRole
|
||||
}
|
||||
|
||||
func (s consoleSTSAssumeRole) RetrieveWithCredContext(cc *credentials.CredContext) (credentials.Value, error) {
|
||||
return s.stsAssumeRole.RetrieveWithCredContext(cc)
|
||||
}
|
||||
|
||||
func (s consoleSTSAssumeRole) Retrieve() (credentials.Value, error) {
|
||||
return s.stsAssumeRole.Retrieve()
|
||||
}
|
||||
@@ -330,7 +311,7 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
|
||||
return s.stsAssumeRole.IsExpired()
|
||||
}
|
||||
|
||||
func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) {
|
||||
func stsCredentials(minioURL, accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) {
|
||||
if accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("credentials endpoint, access and secret key are mandatory for AssumeRoleSTS")
|
||||
}
|
||||
@@ -341,7 +322,7 @@ func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) (
|
||||
DurationSeconds: int(xjwt.GetConsoleSTSDuration().Seconds()),
|
||||
}
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
Client: GetConsoleHTTPClient(clientIP),
|
||||
Client: client,
|
||||
STSEndpoint: minioURL,
|
||||
Options: opts,
|
||||
}
|
||||
@@ -349,51 +330,48 @@ func stsCredentials(minioURL, accessKey, secretKey, location, clientIP string) (
|
||||
return credentials.New(consoleSTSWrapper), nil
|
||||
}
|
||||
|
||||
func NewConsoleCredentials(accessKey, secretKey, location, clientIP string) (*credentials.Credentials, error) {
|
||||
func NewConsoleCredentials(accessKey, secretKey, location string, client *http.Client) (*credentials.Credentials, error) {
|
||||
minioURL := getMinIOServer()
|
||||
|
||||
// Future authentication methods can be added under this switch statement
|
||||
switch {
|
||||
// LDAP authentication for Console
|
||||
case ldap.GetLDAPEnabled():
|
||||
{
|
||||
creds, err := auth.GetCredentialsFromLDAP(GetConsoleHTTPClient(clientIP), minioURL, accessKey, secretKey)
|
||||
if ldap.GetLDAPEnabled() {
|
||||
creds, err := auth.GetCredentialsFromLDAP(client, minioURL, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credContext := &credentials.CredContext{
|
||||
Client: client,
|
||||
}
|
||||
|
||||
// We verify if LDAP credentials are correct and no error is returned
|
||||
_, err = creds.GetWithContext(credContext)
|
||||
|
||||
if err != nil && strings.Contains(strings.ToLower(err.Error()), "not found") {
|
||||
// We try to use STS Credentials in case LDAP credentials are incorrect.
|
||||
stsCreds, errSTS := stsCredentials(minioURL, accessKey, secretKey, location, client)
|
||||
|
||||
// If there is an error with STS too, then we return the original LDAP error
|
||||
if errSTS != nil {
|
||||
LogError("error in STS credentials for LDAP case: %v ", errSTS)
|
||||
|
||||
// We return LDAP result
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
_, err := stsCreds.GetWithContext(credContext)
|
||||
// There is an error with STS credentials, We return the result of LDAP as STS is not a priority in this case.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// We verify if LDAP credentials are correct and no error is returned
|
||||
_, err = creds.Get()
|
||||
|
||||
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, clientIP)
|
||||
|
||||
// If there is an error with STS too, then we return the original LDAP error
|
||||
if errSTS != nil {
|
||||
LogError("error in STS credentials for LDAP case: %v ", errSTS)
|
||||
|
||||
// We return LDAP result
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
_, err := stsCreds.Get()
|
||||
// There is an error with STS credentials, We return the result of LDAP as STS is not a priority in this case.
|
||||
if err != nil {
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
return stsCreds, nil
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
// default authentication for Console is via STS (Security Token Service) against MinIO
|
||||
default:
|
||||
{
|
||||
return stsCredentials(minioURL, accessKey, secretKey, location, clientIP)
|
||||
return stsCreds, nil
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
return stsCredentials(minioURL, accessKey, secretKey, location, client)
|
||||
}
|
||||
|
||||
// getConsoleCredentialsFromSession returns the *consoleCredentials.Login associated to the
|
||||
|
||||
@@ -26,9 +26,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
xcerts "github.com/minio/pkg/v2/certs"
|
||||
"github.com/minio/pkg/v2/env"
|
||||
xnet "github.com/minio/pkg/v2/net"
|
||||
xcerts "github.com/minio/pkg/v3/certs"
|
||||
"github.com/minio/pkg/v3/env"
|
||||
xnet "github.com/minio/pkg/v3/net"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -98,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, ""))
|
||||
}
|
||||
@@ -266,18 +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")
|
||||
}
|
||||
|
||||
func getPrometheusExtraLabels() string {
|
||||
return env.Get(PrometheusExtraLabels, "")
|
||||
}
|
||||
|
||||
func getMaxConcurrentUploadsLimit() int64 {
|
||||
cu, err := strconv.ParseInt(env.Get(ConsoleMaxConcurrentUploads, "10"), 10, 64)
|
||||
if err != nil {
|
||||
@@ -303,3 +291,7 @@ func getConsoleDevMode() bool {
|
||||
func getConsoleAnimatedLogin() bool {
|
||||
return strings.ToLower(env.Get(ConsoleAnimatedLogin, "on")) == "on"
|
||||
}
|
||||
|
||||
func getConsoleBrowserRedirectURL() string {
|
||||
return env.Get(ConsoleBrowserRedirectURL, "")
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -44,9 +46,9 @@ import (
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
|
||||
portal_ui "github.com/minio/console/web-app"
|
||||
"github.com/minio/pkg/v2/env"
|
||||
"github.com/minio/pkg/v2/mimedb"
|
||||
xnet "github.com/minio/pkg/v2/net"
|
||||
"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"
|
||||
@@ -113,72 +115,19 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerLogoutHandlers(api)
|
||||
// Register bucket handlers
|
||||
registerBucketsHandlers(api)
|
||||
// Register all users handlers
|
||||
registerUsersHandlers(api)
|
||||
// Register groups handlers
|
||||
registerGroupsHandlers(api)
|
||||
// Register policies handlers
|
||||
registersPoliciesHandler(api)
|
||||
// Register configurations handlers
|
||||
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 admin info handlers
|
||||
registerAdminInfoHandlers(api)
|
||||
// Register admin arns handlers
|
||||
registerAdminArnsHandlers(api)
|
||||
// Register admin notification endpoints handlers
|
||||
registerAdminNotificationEndpointsHandlers(api)
|
||||
// Register admin Service Account Handlers
|
||||
registerServiceAccountsHandlers(api)
|
||||
// Register admin remote buckets
|
||||
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
|
||||
registerObjectsHandlers(api)
|
||||
// Register Bucket Quota's Handlers
|
||||
registerBucketQuotaHandlers(api)
|
||||
// Register Account handlers
|
||||
registerAccountHandlers(api)
|
||||
|
||||
registerReleasesHandlers(api)
|
||||
|
||||
// Register Bucket Policy's Handlers
|
||||
registerPublicObjectsHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
api.ServerShutdown = func() {}
|
||||
|
||||
// do an initial subnet plan caching
|
||||
fetchLicensePlan()
|
||||
|
||||
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
|
||||
}
|
||||
|
||||
@@ -216,6 +165,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 {
|
||||
@@ -228,6 +268,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)
|
||||
@@ -313,6 +355,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 {
|
||||
@@ -321,11 +369,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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -424,13 +468,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))
|
||||
}
|
||||
|
||||
@@ -507,8 +548,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=\"agpl\"/>", newPlan, 1)
|
||||
indexPageBytes = []byte(indexPageStr)
|
||||
return indexPageBytes
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const (
|
||||
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"
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
// - multipart/form-data
|
||||
//
|
||||
// Produces:
|
||||
// - application/zip
|
||||
// - application/octet-stream
|
||||
// - application/json
|
||||
//
|
||||
|
||||
10190
api/embedded_spec.go
10190
api/embedded_spec.go
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
var (
|
||||
ErrDefault = errors.New("an error occurred, please try again")
|
||||
ErrInvalidLogin = errors.New("invalid Login")
|
||||
ErrInvalidLogin = errors.New("invalid login")
|
||||
ErrForbidden = errors.New("403 Forbidden")
|
||||
ErrBadRequest = errors.New("400 Bad Request")
|
||||
ErrFileTooLarge = errors.New("413 File too Large")
|
||||
@@ -43,7 +43,6 @@ var (
|
||||
ErrGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
ErrPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
ErrPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
ErrPolicyNameContainsSpace = errors.New("error policy name cannot contain spaces")
|
||||
ErrInvalidEncryptionAlgorithm = errors.New("error invalid encryption algorithm")
|
||||
ErrSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
ErrBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
@@ -73,6 +72,7 @@ var (
|
||||
ErrPolicyNotFound = errors.New("policy does not exist")
|
||||
ErrLoginNotAllowed = errors.New("login not allowed")
|
||||
ErrHealthReportFail = errors.New("failure to generate Health report")
|
||||
ErrNetworkError = errors.New("unable to login due to network error")
|
||||
)
|
||||
|
||||
type CodedAPIError struct {
|
||||
@@ -111,6 +111,11 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *CodedAPIError {
|
||||
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()
|
||||
@@ -155,10 +160,6 @@ func ErrorWithContext(ctx context.Context, err ...interface{}) *CodedAPIError {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrPolicyNameContainsSpace) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyNameContainsSpace.Error()
|
||||
}
|
||||
// console invalid session errors
|
||||
if errors.Is(err1, ErrInvalidSession) {
|
||||
errorCode = 401
|
||||
|
||||
@@ -46,25 +46,24 @@ func TestError(t *testing.T) {
|
||||
appErrors := map[string]expectedError{
|
||||
"ErrDefault": {code: 500, err: ErrDefault},
|
||||
|
||||
"ErrForbidden": {code: 403, err: ErrForbidden},
|
||||
"ErrFileTooLarge": {code: 413, err: ErrFileTooLarge},
|
||||
"ErrInvalidSession": {code: 401, err: ErrInvalidSession},
|
||||
"ErrNotFound": {code: 404, err: ErrNotFound},
|
||||
"ErrGroupAlreadyExists": {code: 400, err: ErrGroupAlreadyExists},
|
||||
"ErrInvalidErasureCodingValue": {code: 400, err: ErrInvalidErasureCodingValue},
|
||||
"ErrBucketBodyNotInRequest": {code: 400, err: ErrBucketBodyNotInRequest},
|
||||
"ErrBucketNameNotInRequest": {code: 400, err: ErrBucketNameNotInRequest},
|
||||
"ErrGroupBodyNotInRequest": {code: 400, err: ErrGroupBodyNotInRequest},
|
||||
"ErrGroupNameNotInRequest": {code: 400, err: ErrGroupNameNotInRequest},
|
||||
"ErrPolicyNameNotInRequest": {code: 400, err: ErrPolicyNameNotInRequest},
|
||||
"ErrPolicyBodyNotInRequest": {code: 400, err: ErrPolicyBodyNotInRequest},
|
||||
"ErrInvalidEncryptionAlgorithm": {code: 500, err: ErrInvalidEncryptionAlgorithm},
|
||||
"ErrSSENotConfigured": {code: 404, err: ErrSSENotConfigured},
|
||||
"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},
|
||||
"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},
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// 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 (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/minio/pkg/v2/licverifier"
|
||||
"github.com/minio/pkg/v2/subnet"
|
||||
)
|
||||
|
||||
type SubnetPlan int
|
||||
|
||||
const (
|
||||
PlanAGPL SubnetPlan = iota
|
||||
PlanStandard
|
||||
PlanEnterprise
|
||||
)
|
||||
|
||||
func (sp SubnetPlan) String() string {
|
||||
switch sp {
|
||||
case PlanStandard:
|
||||
return "standard"
|
||||
case PlanEnterprise:
|
||||
return "enterprise"
|
||||
default:
|
||||
return "agpl"
|
||||
}
|
||||
}
|
||||
|
||||
var InstanceLicensePlan = PlanAGPL
|
||||
|
||||
func getLicenseInfo(client http.Client, license string) (*licverifier.LicenseInfo, error) {
|
||||
lv := subnet.LicenseValidator{
|
||||
Client: client,
|
||||
ExpiryGracePeriod: 0,
|
||||
}
|
||||
lv.Init(getConsoleDevMode())
|
||||
return lv.ParseLicense(license)
|
||||
}
|
||||
|
||||
func fetchLicensePlan() {
|
||||
client := GetConsoleHTTPClient("127.0.0.1")
|
||||
licenseInfo, err := getLicenseInfo(*client, os.Getenv(EnvSubnetLicense))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch licenseInfo.Plan {
|
||||
case "STANDARD":
|
||||
InstanceLicensePlan = PlanStandard
|
||||
case "ENTERPRISE":
|
||||
InstanceLicensePlan = PlanEnterprise
|
||||
default:
|
||||
InstanceLicensePlan = PlanAGPL
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AccountChangePasswordHandlerFunc turns a function with the right signature into a account change password handler
|
||||
type AccountChangePasswordHandlerFunc func(AccountChangePasswordParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn AccountChangePasswordHandlerFunc) Handle(params AccountChangePasswordParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// AccountChangePasswordHandler interface for that can handle valid account change password params
|
||||
type AccountChangePasswordHandler interface {
|
||||
Handle(AccountChangePasswordParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewAccountChangePassword creates a new http.Handler for the account change password operation
|
||||
func NewAccountChangePassword(ctx *middleware.Context, handler AccountChangePasswordHandler) *AccountChangePassword {
|
||||
return &AccountChangePassword{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
AccountChangePassword swagger:route POST /account/change-password Account accountChangePassword
|
||||
|
||||
Change password of currently logged in user.
|
||||
*/
|
||||
type AccountChangePassword struct {
|
||||
Context *middleware.Context
|
||||
Handler AccountChangePasswordHandler
|
||||
}
|
||||
|
||||
func (o *AccountChangePassword) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewAccountChangePasswordParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewAccountChangePasswordParams creates a new AccountChangePasswordParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewAccountChangePasswordParams() AccountChangePasswordParams {
|
||||
|
||||
return AccountChangePasswordParams{}
|
||||
}
|
||||
|
||||
// AccountChangePasswordParams contains all the bound params for the account change password operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters AccountChangePassword
|
||||
type AccountChangePasswordParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.AccountChangePasswordRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewAccountChangePasswordParams() beforehand.
|
||||
func (o *AccountChangePasswordParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.AccountChangePasswordRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AccountChangePasswordNoContentCode is the HTTP code returned for type AccountChangePasswordNoContent
|
||||
const AccountChangePasswordNoContentCode int = 204
|
||||
|
||||
/*
|
||||
AccountChangePasswordNoContent A successful login.
|
||||
|
||||
swagger:response accountChangePasswordNoContent
|
||||
*/
|
||||
type AccountChangePasswordNoContent struct {
|
||||
}
|
||||
|
||||
// NewAccountChangePasswordNoContent creates AccountChangePasswordNoContent with default headers values
|
||||
func NewAccountChangePasswordNoContent() *AccountChangePasswordNoContent {
|
||||
|
||||
return &AccountChangePasswordNoContent{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AccountChangePasswordNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(204)
|
||||
}
|
||||
|
||||
/*
|
||||
AccountChangePasswordDefault Generic error response.
|
||||
|
||||
swagger:response accountChangePasswordDefault
|
||||
*/
|
||||
type AccountChangePasswordDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAccountChangePasswordDefault creates AccountChangePasswordDefault with default headers values
|
||||
func NewAccountChangePasswordDefault(code int) *AccountChangePasswordDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &AccountChangePasswordDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the account change password default response
|
||||
func (o *AccountChangePasswordDefault) WithStatusCode(code int) *AccountChangePasswordDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the account change password default response
|
||||
func (o *AccountChangePasswordDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the account change password default response
|
||||
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.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AccountChangePasswordDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// AccountChangePasswordURL generates an URL for the account change password operation
|
||||
type AccountChangePasswordURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AccountChangePasswordURL) WithBasePath(bp string) *AccountChangePasswordURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AccountChangePasswordURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *AccountChangePasswordURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/account/change-password"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *AccountChangePasswordURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *AccountChangePasswordURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *AccountChangePasswordURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on AccountChangePasswordURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on AccountChangePasswordURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *AccountChangePasswordURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// ChangeUserPasswordHandlerFunc turns a function with the right signature into a change user password handler
|
||||
type ChangeUserPasswordHandlerFunc func(ChangeUserPasswordParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn ChangeUserPasswordHandlerFunc) Handle(params ChangeUserPasswordParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// ChangeUserPasswordHandler interface for that can handle valid change user password params
|
||||
type ChangeUserPasswordHandler interface {
|
||||
Handle(ChangeUserPasswordParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewChangeUserPassword creates a new http.Handler for the change user password operation
|
||||
func NewChangeUserPassword(ctx *middleware.Context, handler ChangeUserPasswordHandler) *ChangeUserPassword {
|
||||
return &ChangeUserPassword{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
ChangeUserPassword swagger:route POST /account/change-user-password Account changeUserPassword
|
||||
|
||||
Change password of currently logged in user.
|
||||
*/
|
||||
type ChangeUserPassword struct {
|
||||
Context *middleware.Context
|
||||
Handler ChangeUserPasswordHandler
|
||||
}
|
||||
|
||||
func (o *ChangeUserPassword) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewChangeUserPasswordParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewChangeUserPasswordParams creates a new ChangeUserPasswordParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewChangeUserPasswordParams() ChangeUserPasswordParams {
|
||||
|
||||
return ChangeUserPasswordParams{}
|
||||
}
|
||||
|
||||
// ChangeUserPasswordParams contains all the bound params for the change user password operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters ChangeUserPassword
|
||||
type ChangeUserPasswordParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.ChangeUserPasswordRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewChangeUserPasswordParams() beforehand.
|
||||
func (o *ChangeUserPasswordParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.ChangeUserPasswordRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// ChangeUserPasswordCreatedCode is the HTTP code returned for type ChangeUserPasswordCreated
|
||||
const ChangeUserPasswordCreatedCode int = 201
|
||||
|
||||
/*
|
||||
ChangeUserPasswordCreated Password successfully changed.
|
||||
|
||||
swagger:response changeUserPasswordCreated
|
||||
*/
|
||||
type ChangeUserPasswordCreated struct {
|
||||
}
|
||||
|
||||
// NewChangeUserPasswordCreated creates ChangeUserPasswordCreated with default headers values
|
||||
func NewChangeUserPasswordCreated() *ChangeUserPasswordCreated {
|
||||
|
||||
return &ChangeUserPasswordCreated{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ChangeUserPasswordCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(201)
|
||||
}
|
||||
|
||||
/*
|
||||
ChangeUserPasswordDefault Generic error response.
|
||||
|
||||
swagger:response changeUserPasswordDefault
|
||||
*/
|
||||
type ChangeUserPasswordDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewChangeUserPasswordDefault creates ChangeUserPasswordDefault with default headers values
|
||||
func NewChangeUserPasswordDefault(code int) *ChangeUserPasswordDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &ChangeUserPasswordDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the change user password default response
|
||||
func (o *ChangeUserPasswordDefault) WithStatusCode(code int) *ChangeUserPasswordDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the change user password default response
|
||||
func (o *ChangeUserPasswordDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the change user password default response
|
||||
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.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ChangeUserPasswordDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package account
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// ChangeUserPasswordURL generates an URL for the change user password operation
|
||||
type ChangeUserPasswordURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *ChangeUserPasswordURL) WithBasePath(bp string) *ChangeUserPasswordURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *ChangeUserPasswordURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *ChangeUserPasswordURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/account/change-user-password"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *ChangeUserPasswordURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *ChangeUserPasswordURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *ChangeUserPasswordURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on ChangeUserPasswordURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on ChangeUserPasswordURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *ChangeUserPasswordURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddBucketLifecycleHandlerFunc turns a function with the right signature into a add bucket lifecycle handler
|
||||
type AddBucketLifecycleHandlerFunc func(AddBucketLifecycleParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn AddBucketLifecycleHandlerFunc) Handle(params AddBucketLifecycleParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// AddBucketLifecycleHandler interface for that can handle valid add bucket lifecycle params
|
||||
type AddBucketLifecycleHandler interface {
|
||||
Handle(AddBucketLifecycleParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewAddBucketLifecycle creates a new http.Handler for the add bucket lifecycle operation
|
||||
func NewAddBucketLifecycle(ctx *middleware.Context, handler AddBucketLifecycleHandler) *AddBucketLifecycle {
|
||||
return &AddBucketLifecycle{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
AddBucketLifecycle swagger:route POST /buckets/{bucket_name}/lifecycle Bucket addBucketLifecycle
|
||||
|
||||
Add Bucket Lifecycle
|
||||
*/
|
||||
type AddBucketLifecycle struct {
|
||||
Context *middleware.Context
|
||||
Handler AddBucketLifecycleHandler
|
||||
}
|
||||
|
||||
func (o *AddBucketLifecycle) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewAddBucketLifecycleParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewAddBucketLifecycleParams creates a new AddBucketLifecycleParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewAddBucketLifecycleParams() AddBucketLifecycleParams {
|
||||
|
||||
return AddBucketLifecycleParams{}
|
||||
}
|
||||
|
||||
// AddBucketLifecycleParams contains all the bound params for the add bucket lifecycle operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters AddBucketLifecycle
|
||||
type AddBucketLifecycleParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.AddBucketLifecycle
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewAddBucketLifecycleParams() beforehand.
|
||||
func (o *AddBucketLifecycleParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.AddBucketLifecycle
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucketName binds and validates parameter BucketName from path.
|
||||
func (o *AddBucketLifecycleParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.BucketName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddBucketLifecycleCreatedCode is the HTTP code returned for type AddBucketLifecycleCreated
|
||||
const AddBucketLifecycleCreatedCode int = 201
|
||||
|
||||
/*
|
||||
AddBucketLifecycleCreated A successful response.
|
||||
|
||||
swagger:response addBucketLifecycleCreated
|
||||
*/
|
||||
type AddBucketLifecycleCreated struct {
|
||||
}
|
||||
|
||||
// NewAddBucketLifecycleCreated creates AddBucketLifecycleCreated with default headers values
|
||||
func NewAddBucketLifecycleCreated() *AddBucketLifecycleCreated {
|
||||
|
||||
return &AddBucketLifecycleCreated{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddBucketLifecycleCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(201)
|
||||
}
|
||||
|
||||
/*
|
||||
AddBucketLifecycleDefault Generic error response.
|
||||
|
||||
swagger:response addBucketLifecycleDefault
|
||||
*/
|
||||
type AddBucketLifecycleDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAddBucketLifecycleDefault creates AddBucketLifecycleDefault with default headers values
|
||||
func NewAddBucketLifecycleDefault(code int) *AddBucketLifecycleDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &AddBucketLifecycleDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the add bucket lifecycle default response
|
||||
func (o *AddBucketLifecycleDefault) WithStatusCode(code int) *AddBucketLifecycleDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the add bucket lifecycle default response
|
||||
func (o *AddBucketLifecycleDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the add bucket lifecycle default response
|
||||
func (o *AddBucketLifecycleDefault) WithPayload(payload *models.APIError) *AddBucketLifecycleDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the add bucket lifecycle default response
|
||||
func (o *AddBucketLifecycleDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddBucketLifecycleDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AddBucketLifecycleURL generates an URL for the add bucket lifecycle operation
|
||||
type AddBucketLifecycleURL struct {
|
||||
BucketName string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddBucketLifecycleURL) WithBasePath(bp string) *AddBucketLifecycleURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddBucketLifecycleURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *AddBucketLifecycleURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{bucket_name}/lifecycle"
|
||||
|
||||
bucketName := o.BucketName
|
||||
if bucketName != "" {
|
||||
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
|
||||
} else {
|
||||
return nil, errors.New("bucketName is required on AddBucketLifecycleURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *AddBucketLifecycleURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *AddBucketLifecycleURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *AddBucketLifecycleURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on AddBucketLifecycleURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on AddBucketLifecycleURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *AddBucketLifecycleURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddMultiBucketLifecycleHandlerFunc turns a function with the right signature into a add multi bucket lifecycle handler
|
||||
type AddMultiBucketLifecycleHandlerFunc func(AddMultiBucketLifecycleParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn AddMultiBucketLifecycleHandlerFunc) Handle(params AddMultiBucketLifecycleParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// AddMultiBucketLifecycleHandler interface for that can handle valid add multi bucket lifecycle params
|
||||
type AddMultiBucketLifecycleHandler interface {
|
||||
Handle(AddMultiBucketLifecycleParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewAddMultiBucketLifecycle creates a new http.Handler for the add multi bucket lifecycle operation
|
||||
func NewAddMultiBucketLifecycle(ctx *middleware.Context, handler AddMultiBucketLifecycleHandler) *AddMultiBucketLifecycle {
|
||||
return &AddMultiBucketLifecycle{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
AddMultiBucketLifecycle swagger:route POST /buckets/multi-lifecycle Bucket addMultiBucketLifecycle
|
||||
|
||||
Add Multi Bucket Lifecycle
|
||||
*/
|
||||
type AddMultiBucketLifecycle struct {
|
||||
Context *middleware.Context
|
||||
Handler AddMultiBucketLifecycleHandler
|
||||
}
|
||||
|
||||
func (o *AddMultiBucketLifecycle) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewAddMultiBucketLifecycleParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewAddMultiBucketLifecycleParams creates a new AddMultiBucketLifecycleParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewAddMultiBucketLifecycleParams() AddMultiBucketLifecycleParams {
|
||||
|
||||
return AddMultiBucketLifecycleParams{}
|
||||
}
|
||||
|
||||
// AddMultiBucketLifecycleParams contains all the bound params for the add multi bucket lifecycle operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters AddMultiBucketLifecycle
|
||||
type AddMultiBucketLifecycleParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.AddMultiBucketLifecycle
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewAddMultiBucketLifecycleParams() beforehand.
|
||||
func (o *AddMultiBucketLifecycleParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.AddMultiBucketLifecycle
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddMultiBucketLifecycleOKCode is the HTTP code returned for type AddMultiBucketLifecycleOK
|
||||
const AddMultiBucketLifecycleOKCode int = 200
|
||||
|
||||
/*
|
||||
AddMultiBucketLifecycleOK A successful response.
|
||||
|
||||
swagger:response addMultiBucketLifecycleOK
|
||||
*/
|
||||
type AddMultiBucketLifecycleOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.MultiLifecycleResult `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAddMultiBucketLifecycleOK creates AddMultiBucketLifecycleOK with default headers values
|
||||
func NewAddMultiBucketLifecycleOK() *AddMultiBucketLifecycleOK {
|
||||
|
||||
return &AddMultiBucketLifecycleOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the add multi bucket lifecycle o k response
|
||||
func (o *AddMultiBucketLifecycleOK) WithPayload(payload *models.MultiLifecycleResult) *AddMultiBucketLifecycleOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the add multi bucket lifecycle o k response
|
||||
func (o *AddMultiBucketLifecycleOK) SetPayload(payload *models.MultiLifecycleResult) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddMultiBucketLifecycleOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
AddMultiBucketLifecycleDefault Generic error response.
|
||||
|
||||
swagger:response addMultiBucketLifecycleDefault
|
||||
*/
|
||||
type AddMultiBucketLifecycleDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAddMultiBucketLifecycleDefault creates AddMultiBucketLifecycleDefault with default headers values
|
||||
func NewAddMultiBucketLifecycleDefault(code int) *AddMultiBucketLifecycleDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &AddMultiBucketLifecycleDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the add multi bucket lifecycle default response
|
||||
func (o *AddMultiBucketLifecycleDefault) WithStatusCode(code int) *AddMultiBucketLifecycleDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the add multi bucket lifecycle default response
|
||||
func (o *AddMultiBucketLifecycleDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the add multi bucket lifecycle default response
|
||||
func (o *AddMultiBucketLifecycleDefault) WithPayload(payload *models.APIError) *AddMultiBucketLifecycleDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the add multi bucket lifecycle default response
|
||||
func (o *AddMultiBucketLifecycleDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddMultiBucketLifecycleDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// AddMultiBucketLifecycleURL generates an URL for the add multi bucket lifecycle operation
|
||||
type AddMultiBucketLifecycleURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddMultiBucketLifecycleURL) WithBasePath(bp string) *AddMultiBucketLifecycleURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddMultiBucketLifecycleURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *AddMultiBucketLifecycleURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/multi-lifecycle"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *AddMultiBucketLifecycleURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *AddMultiBucketLifecycleURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *AddMultiBucketLifecycleURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on AddMultiBucketLifecycleURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on AddMultiBucketLifecycleURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *AddMultiBucketLifecycleURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddRemoteBucketHandlerFunc turns a function with the right signature into a add remote bucket handler
|
||||
type AddRemoteBucketHandlerFunc func(AddRemoteBucketParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn AddRemoteBucketHandlerFunc) Handle(params AddRemoteBucketParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// AddRemoteBucketHandler interface for that can handle valid add remote bucket params
|
||||
type AddRemoteBucketHandler interface {
|
||||
Handle(AddRemoteBucketParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewAddRemoteBucket creates a new http.Handler for the add remote bucket operation
|
||||
func NewAddRemoteBucket(ctx *middleware.Context, handler AddRemoteBucketHandler) *AddRemoteBucket {
|
||||
return &AddRemoteBucket{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
AddRemoteBucket swagger:route POST /remote-buckets Bucket addRemoteBucket
|
||||
|
||||
Add Remote Bucket
|
||||
*/
|
||||
type AddRemoteBucket struct {
|
||||
Context *middleware.Context
|
||||
Handler AddRemoteBucketHandler
|
||||
}
|
||||
|
||||
func (o *AddRemoteBucket) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewAddRemoteBucketParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewAddRemoteBucketParams creates a new AddRemoteBucketParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewAddRemoteBucketParams() AddRemoteBucketParams {
|
||||
|
||||
return AddRemoteBucketParams{}
|
||||
}
|
||||
|
||||
// AddRemoteBucketParams contains all the bound params for the add remote bucket operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters AddRemoteBucket
|
||||
type AddRemoteBucketParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.CreateRemoteBucket
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewAddRemoteBucketParams() beforehand.
|
||||
func (o *AddRemoteBucketParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.CreateRemoteBucket
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// AddRemoteBucketCreatedCode is the HTTP code returned for type AddRemoteBucketCreated
|
||||
const AddRemoteBucketCreatedCode int = 201
|
||||
|
||||
/*
|
||||
AddRemoteBucketCreated A successful response.
|
||||
|
||||
swagger:response addRemoteBucketCreated
|
||||
*/
|
||||
type AddRemoteBucketCreated struct {
|
||||
}
|
||||
|
||||
// NewAddRemoteBucketCreated creates AddRemoteBucketCreated with default headers values
|
||||
func NewAddRemoteBucketCreated() *AddRemoteBucketCreated {
|
||||
|
||||
return &AddRemoteBucketCreated{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddRemoteBucketCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(201)
|
||||
}
|
||||
|
||||
/*
|
||||
AddRemoteBucketDefault Generic error response.
|
||||
|
||||
swagger:response addRemoteBucketDefault
|
||||
*/
|
||||
type AddRemoteBucketDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewAddRemoteBucketDefault creates AddRemoteBucketDefault with default headers values
|
||||
func NewAddRemoteBucketDefault(code int) *AddRemoteBucketDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &AddRemoteBucketDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the add remote bucket default response
|
||||
func (o *AddRemoteBucketDefault) WithStatusCode(code int) *AddRemoteBucketDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the add remote bucket default response
|
||||
func (o *AddRemoteBucketDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the add remote bucket default response
|
||||
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.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *AddRemoteBucketDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// AddRemoteBucketURL generates an URL for the add remote bucket operation
|
||||
type AddRemoteBucketURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddRemoteBucketURL) WithBasePath(bp string) *AddRemoteBucketURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *AddRemoteBucketURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *AddRemoteBucketURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/remote-buckets"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *AddRemoteBucketURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *AddRemoteBucketURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *AddRemoteBucketURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on AddRemoteBucketURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on AddRemoteBucketURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *AddRemoteBucketURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// BucketSetPolicyHandlerFunc turns a function with the right signature into a bucket set policy handler
|
||||
type BucketSetPolicyHandlerFunc func(BucketSetPolicyParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn BucketSetPolicyHandlerFunc) Handle(params BucketSetPolicyParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// BucketSetPolicyHandler interface for that can handle valid bucket set policy params
|
||||
type BucketSetPolicyHandler interface {
|
||||
Handle(BucketSetPolicyParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewBucketSetPolicy creates a new http.Handler for the bucket set policy operation
|
||||
func NewBucketSetPolicy(ctx *middleware.Context, handler BucketSetPolicyHandler) *BucketSetPolicy {
|
||||
return &BucketSetPolicy{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
BucketSetPolicy swagger:route PUT /buckets/{name}/set-policy Bucket bucketSetPolicy
|
||||
|
||||
Bucket Set Policy
|
||||
*/
|
||||
type BucketSetPolicy struct {
|
||||
Context *middleware.Context
|
||||
Handler BucketSetPolicyHandler
|
||||
}
|
||||
|
||||
func (o *BucketSetPolicy) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewBucketSetPolicyParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewBucketSetPolicyParams creates a new BucketSetPolicyParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewBucketSetPolicyParams() BucketSetPolicyParams {
|
||||
|
||||
return BucketSetPolicyParams{}
|
||||
}
|
||||
|
||||
// BucketSetPolicyParams contains all the bound params for the bucket set policy operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters BucketSetPolicy
|
||||
type BucketSetPolicyParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.SetBucketPolicyRequest
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Name string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewBucketSetPolicyParams() beforehand.
|
||||
func (o *BucketSetPolicyParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.SetBucketPolicyRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
|
||||
rName, rhkName, _ := route.Params.GetOK("name")
|
||||
if err := o.bindName(rName, rhkName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindName binds and validates parameter Name from path.
|
||||
func (o *BucketSetPolicyParams) bindName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Name = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// BucketSetPolicyOKCode is the HTTP code returned for type BucketSetPolicyOK
|
||||
const BucketSetPolicyOKCode int = 200
|
||||
|
||||
/*
|
||||
BucketSetPolicyOK A successful response.
|
||||
|
||||
swagger:response bucketSetPolicyOK
|
||||
*/
|
||||
type BucketSetPolicyOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Bucket `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewBucketSetPolicyOK creates BucketSetPolicyOK with default headers values
|
||||
func NewBucketSetPolicyOK() *BucketSetPolicyOK {
|
||||
|
||||
return &BucketSetPolicyOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the bucket set policy o k response
|
||||
func (o *BucketSetPolicyOK) WithPayload(payload *models.Bucket) *BucketSetPolicyOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the bucket set policy o k response
|
||||
func (o *BucketSetPolicyOK) SetPayload(payload *models.Bucket) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *BucketSetPolicyOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BucketSetPolicyDefault Generic error response.
|
||||
|
||||
swagger:response bucketSetPolicyDefault
|
||||
*/
|
||||
type BucketSetPolicyDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewBucketSetPolicyDefault creates BucketSetPolicyDefault with default headers values
|
||||
func NewBucketSetPolicyDefault(code int) *BucketSetPolicyDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &BucketSetPolicyDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the bucket set policy default response
|
||||
func (o *BucketSetPolicyDefault) WithStatusCode(code int) *BucketSetPolicyDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the bucket set policy default response
|
||||
func (o *BucketSetPolicyDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the bucket set policy default response
|
||||
func (o *BucketSetPolicyDefault) WithPayload(payload *models.APIError) *BucketSetPolicyDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the bucket set policy default response
|
||||
func (o *BucketSetPolicyDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *BucketSetPolicyDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BucketSetPolicyURL generates an URL for the bucket set policy operation
|
||||
type BucketSetPolicyURL struct {
|
||||
Name string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *BucketSetPolicyURL) WithBasePath(bp string) *BucketSetPolicyURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *BucketSetPolicyURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *BucketSetPolicyURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{name}/set-policy"
|
||||
|
||||
name := o.Name
|
||||
if name != "" {
|
||||
_path = strings.Replace(_path, "{name}", name, -1)
|
||||
} else {
|
||||
return nil, errors.New("name is required on BucketSetPolicyURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *BucketSetPolicyURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *BucketSetPolicyURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *BucketSetPolicyURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on BucketSetPolicyURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on BucketSetPolicyURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *BucketSetPolicyURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// CreateBucketEventHandlerFunc turns a function with the right signature into a create bucket event handler
|
||||
type CreateBucketEventHandlerFunc func(CreateBucketEventParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn CreateBucketEventHandlerFunc) Handle(params CreateBucketEventParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// CreateBucketEventHandler interface for that can handle valid create bucket event params
|
||||
type CreateBucketEventHandler interface {
|
||||
Handle(CreateBucketEventParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewCreateBucketEvent creates a new http.Handler for the create bucket event operation
|
||||
func NewCreateBucketEvent(ctx *middleware.Context, handler CreateBucketEventHandler) *CreateBucketEvent {
|
||||
return &CreateBucketEvent{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
CreateBucketEvent swagger:route POST /buckets/{bucket_name}/events Bucket createBucketEvent
|
||||
|
||||
Create Bucket Event
|
||||
*/
|
||||
type CreateBucketEvent struct {
|
||||
Context *middleware.Context
|
||||
Handler CreateBucketEventHandler
|
||||
}
|
||||
|
||||
func (o *CreateBucketEvent) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewCreateBucketEventParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewCreateBucketEventParams creates a new CreateBucketEventParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewCreateBucketEventParams() CreateBucketEventParams {
|
||||
|
||||
return CreateBucketEventParams{}
|
||||
}
|
||||
|
||||
// CreateBucketEventParams contains all the bound params for the create bucket event operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters CreateBucketEvent
|
||||
type CreateBucketEventParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.BucketEventRequest
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewCreateBucketEventParams() beforehand.
|
||||
func (o *CreateBucketEventParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.BucketEventRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucketName binds and validates parameter BucketName from path.
|
||||
func (o *CreateBucketEventParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.BucketName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// CreateBucketEventCreatedCode is the HTTP code returned for type CreateBucketEventCreated
|
||||
const CreateBucketEventCreatedCode int = 201
|
||||
|
||||
/*
|
||||
CreateBucketEventCreated A successful response.
|
||||
|
||||
swagger:response createBucketEventCreated
|
||||
*/
|
||||
type CreateBucketEventCreated struct {
|
||||
}
|
||||
|
||||
// NewCreateBucketEventCreated creates CreateBucketEventCreated with default headers values
|
||||
func NewCreateBucketEventCreated() *CreateBucketEventCreated {
|
||||
|
||||
return &CreateBucketEventCreated{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *CreateBucketEventCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(201)
|
||||
}
|
||||
|
||||
/*
|
||||
CreateBucketEventDefault Generic error response.
|
||||
|
||||
swagger:response createBucketEventDefault
|
||||
*/
|
||||
type CreateBucketEventDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.APIError `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewCreateBucketEventDefault creates CreateBucketEventDefault with default headers values
|
||||
func NewCreateBucketEventDefault(code int) *CreateBucketEventDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &CreateBucketEventDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the create bucket event default response
|
||||
func (o *CreateBucketEventDefault) WithStatusCode(code int) *CreateBucketEventDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the create bucket event default response
|
||||
func (o *CreateBucketEventDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the create bucket event default response
|
||||
func (o *CreateBucketEventDefault) WithPayload(payload *models.APIError) *CreateBucketEventDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the create bucket event default response
|
||||
func (o *CreateBucketEventDefault) SetPayload(payload *models.APIError) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *CreateBucketEventDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateBucketEventURL generates an URL for the create bucket event operation
|
||||
type CreateBucketEventURL struct {
|
||||
BucketName string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *CreateBucketEventURL) WithBasePath(bp string) *CreateBucketEventURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *CreateBucketEventURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *CreateBucketEventURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{bucket_name}/events"
|
||||
|
||||
bucketName := o.BucketName
|
||||
if bucketName != "" {
|
||||
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
|
||||
} else {
|
||||
return nil, errors.New("bucketName is required on CreateBucketEventURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *CreateBucketEventURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *CreateBucketEventURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *CreateBucketEventURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on CreateBucketEventURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on CreateBucketEventURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *CreateBucketEventURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// DeleteAccessRuleWithBucketHandlerFunc turns a function with the right signature into a delete access rule with bucket handler
|
||||
type DeleteAccessRuleWithBucketHandlerFunc func(DeleteAccessRuleWithBucketParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn DeleteAccessRuleWithBucketHandlerFunc) Handle(params DeleteAccessRuleWithBucketParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// DeleteAccessRuleWithBucketHandler interface for that can handle valid delete access rule with bucket params
|
||||
type DeleteAccessRuleWithBucketHandler interface {
|
||||
Handle(DeleteAccessRuleWithBucketParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewDeleteAccessRuleWithBucket creates a new http.Handler for the delete access rule with bucket operation
|
||||
func NewDeleteAccessRuleWithBucket(ctx *middleware.Context, handler DeleteAccessRuleWithBucketHandler) *DeleteAccessRuleWithBucket {
|
||||
return &DeleteAccessRuleWithBucket{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
DeleteAccessRuleWithBucket swagger:route DELETE /bucket/{bucket}/access-rules Bucket deleteAccessRuleWithBucket
|
||||
|
||||
Delete Access Rule From Given Bucket
|
||||
*/
|
||||
type DeleteAccessRuleWithBucket struct {
|
||||
Context *middleware.Context
|
||||
Handler DeleteAccessRuleWithBucketHandler
|
||||
}
|
||||
|
||||
func (o *DeleteAccessRuleWithBucket) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewDeleteAccessRuleWithBucketParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package bucket
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewDeleteAccessRuleWithBucketParams creates a new DeleteAccessRuleWithBucketParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewDeleteAccessRuleWithBucketParams() DeleteAccessRuleWithBucketParams {
|
||||
|
||||
return DeleteAccessRuleWithBucketParams{}
|
||||
}
|
||||
|
||||
// DeleteAccessRuleWithBucketParams contains all the bound params for the delete access rule with bucket operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters DeleteAccessRuleWithBucket
|
||||
type DeleteAccessRuleWithBucketParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Bucket string
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Prefix *models.PrefixWrapper
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewDeleteAccessRuleWithBucketParams() beforehand.
|
||||
func (o *DeleteAccessRuleWithBucketParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
rBucket, rhkBucket, _ := route.Params.GetOK("bucket")
|
||||
if err := o.bindBucket(rBucket, rhkBucket, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.PrefixWrapper
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("prefix", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("prefix", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
ctx := validate.WithOperationRequest(r.Context())
|
||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Prefix = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("prefix", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucket binds and validates parameter Bucket from path.
|
||||
func (o *DeleteAccessRuleWithBucketParams) bindBucket(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Bucket = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user