Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf8472b04c | ||
|
|
317a7ebbd3 | ||
|
|
63f4150232 | ||
|
|
3bfc2556fc | ||
|
|
511cc47d2b | ||
|
|
9660650f41 | ||
|
|
9ac754d4de | ||
|
|
646318e1f6 | ||
|
|
beb1ac7d04 | ||
|
|
f3a9311374 |
2
go.mod
2
go.mod
@@ -18,7 +18,7 @@ require (
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/mc v0.0.0-20200415193718-68b638f2f96c
|
||||
github.com/minio/minio v0.0.0-20200428222040-c3c3e9087bc1
|
||||
github.com/minio/minio v0.0.0-20200501193630-d1c8e9f31ba0
|
||||
github.com/minio/minio-go/v6 v6.0.55-0.20200424204115-7506d2996b22
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
|
||||
20
go.sum
20
go.sum
@@ -30,7 +30,6 @@ github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skP
|
||||
github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 h1:nWDRPCyCltiTsANwC/n3QZH7Vww33Npq9MKqlwRzI/c=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@@ -42,7 +41,6 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/aws/aws-sdk-go v1.20.21/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2 h1:M+TYzBcNIRyzPRg66ndEqUMd7oWDmhvdQmaPC6EZNwM=
|
||||
github.com/bcicen/jstream v0.0.0-20190220045926-16c1f8af81c2/go.mod h1:RDu/qcrnpEdJC/p8tx34+YBFqqX71lB7dOX9QE+ZC4M=
|
||||
@@ -403,10 +401,9 @@ github.com/minio/lsync v1.0.1 h1:AVvILxA976xc27hstd1oR+X9PQG0sPSom1MNb1ImfUs=
|
||||
github.com/minio/lsync v1.0.1/go.mod h1:tCFzfo0dlvdGl70IT4IAK/5Wtgb0/BrTmo/jE8pArKA=
|
||||
github.com/minio/mc v0.0.0-20200415193718-68b638f2f96c h1:JLr0fYpCleodj9nGB5hfsJU2zPdnNQKqa2bYsIvPhVw=
|
||||
github.com/minio/mc v0.0.0-20200415193718-68b638f2f96c/go.mod h1:l9PuOY62zT7AQJqopDjfo/T22AIBJSb2yXPVZf4RlhM=
|
||||
github.com/minio/minio v0.0.0-20200415191640-bde0f444dbab h1:9hlqghJl3e3HorXa6ADWsz6ECq790t4iQs07VD9JctM=
|
||||
github.com/minio/minio v0.0.0-20200415191640-bde0f444dbab/go.mod h1:v8oQPMMaTkjDwp5cOz1WCElA4Ik+X+0y4On+VMk0fis=
|
||||
github.com/minio/minio v0.0.0-20200428222040-c3c3e9087bc1 h1:DQjH/653WCerOeZCp3BxAgkmRiQybHYiprbTFs+brgA=
|
||||
github.com/minio/minio v0.0.0-20200428222040-c3c3e9087bc1/go.mod h1:HxnN5FYGIii8ZH6d+LH5UNOSSIonbJkYPqP6gWelVO0=
|
||||
github.com/minio/minio v0.0.0-20200501193630-d1c8e9f31ba0 h1:QxIz36O01LbKqJiz6HKeKCOC2afgydspkpahQ807msY=
|
||||
github.com/minio/minio v0.0.0-20200501193630-d1c8e9f31ba0/go.mod h1:Vhlqz7Se0EgpgFiVxpvzF4Zz/h2LMx+EPKH96Aera5U=
|
||||
github.com/minio/minio-go/v6 v6.0.53 h1:8jzpwiOzZ5Iz7/goFWqNZRICbyWYShbb5rARjrnSCNI=
|
||||
github.com/minio/minio-go/v6 v6.0.53/go.mod h1:DIvC/IApeHX8q1BAMVCXSXwpmrmM+I+iBvhvztQorfI=
|
||||
github.com/minio/minio-go/v6 v6.0.55-0.20200424204115-7506d2996b22 h1:nZEve4vdUhwHBoV18zRvPDgjL6NYyDJE5QJvz3l9bRs=
|
||||
@@ -555,6 +552,7 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRN
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -596,6 +594,7 @@ github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
@@ -626,6 +625,7 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f h1:kz4KIr+xcPUsI3VMoqWfPMvtnJ6MGfiVwsWSVzphMO4=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
|
||||
@@ -635,6 +635,8 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -665,6 +667,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -713,7 +717,13 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e h1:nOOVVcLC+/3MeovP40q5lCiWmP1Z1DaN8yn8ngU63hw=
|
||||
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32 h1:Xvf3ZQTm5bjXPxhI7g+dwqsCqadK1rcNtwtszuatetk=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
|
||||
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
|
||||
@@ -27,22 +27,22 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ServiceAccount service account
|
||||
// ServiceAccountRequest service account request
|
||||
//
|
||||
// swagger:model serviceAccount
|
||||
type ServiceAccount struct {
|
||||
// swagger:model serviceAccountRequest
|
||||
type ServiceAccountRequest struct {
|
||||
|
||||
// policy to be applied to the Service Account if any
|
||||
Policy string `json:"policy,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this service account
|
||||
func (m *ServiceAccount) Validate(formats strfmt.Registry) error {
|
||||
// Validate validates this service account request
|
||||
func (m *ServiceAccountRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ServiceAccount) MarshalBinary() ([]byte, error) {
|
||||
func (m *ServiceAccountRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -50,8 +50,8 @@ func (m *ServiceAccount) MarshalBinary() ([]byte, error) {
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ServiceAccount) UnmarshalBinary(b []byte) error {
|
||||
var res ServiceAccount
|
||||
func (m *ServiceAccountRequest) UnmarshalBinary(b []byte) error {
|
||||
var res ServiceAccountRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
37
models/service_accounts.go
Normal file
37
models/service_accounts.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// ServiceAccounts service accounts
|
||||
//
|
||||
// swagger:model serviceAccounts
|
||||
type ServiceAccounts []string
|
||||
|
||||
// Validate validates this service accounts
|
||||
func (m ServiceAccounts) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -20,6 +20,7 @@
|
||||
"@types/superagent": "^4.1.4",
|
||||
"@types/webpack-env": "^1.14.1",
|
||||
"@types/websocket": "^1.0.0",
|
||||
"ansi-to-react": "^6.0.5",
|
||||
"codemirror": "^5.52.2",
|
||||
"history": "^4.10.1",
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import storage from "local-storage-fallback";
|
||||
import request from "superagent";
|
||||
import get from "lodash/get";
|
||||
|
||||
export class API {
|
||||
invoke(method: string, url: string, data?: object) {
|
||||
@@ -36,10 +37,16 @@ export class API {
|
||||
|
||||
onError(err: any) {
|
||||
if (err.status) {
|
||||
const errMessage: string =
|
||||
(err.response.body && err.response.body.error) ||
|
||||
err.status.toString(10);
|
||||
return Promise.reject(errMessage);
|
||||
const errMessage = get(
|
||||
err.response,
|
||||
"body.message",
|
||||
err.status.toString()
|
||||
);
|
||||
|
||||
const throwMessage =
|
||||
errMessage.charAt(0).toUpperCase() + errMessage.slice(1);
|
||||
|
||||
return Promise.reject(throwMessage);
|
||||
} else {
|
||||
return Promise.reject("Unknown error");
|
||||
}
|
||||
|
||||
@@ -13,13 +13,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
createRef,
|
||||
ChangeEvent,
|
||||
useCallback
|
||||
} from "react";
|
||||
import React, { useState, useEffect, createRef, ChangeEvent } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import get from "lodash/get";
|
||||
@@ -69,15 +63,19 @@ const CSVMultiSelector = ({
|
||||
const bottomList = createRef<HTMLDivElement>();
|
||||
|
||||
// Use effect to get the initial values from props
|
||||
useCallback(() => {
|
||||
if (currentElements.length === 1 && currentElements[0] === "") {
|
||||
const elementsSplitted = elements.split(",");
|
||||
if (elementsSplitted[elementsSplitted.length - 1].trim() !== "") {
|
||||
elementsSplitted.push("");
|
||||
}
|
||||
setCurrentElements(elementsSplitted);
|
||||
useEffect(() => {
|
||||
if (
|
||||
currentElements.length === 1 &&
|
||||
currentElements[0] === "" &&
|
||||
elements &&
|
||||
elements !== ""
|
||||
) {
|
||||
const elementsSplit = elements.split(",");
|
||||
elementsSplit.push("");
|
||||
|
||||
setCurrentElements(elementsSplit);
|
||||
}
|
||||
}, [elements, setCurrentElements, currentElements]);
|
||||
}, [elements, currentElements]);
|
||||
|
||||
// Use effect to send new values to onChange
|
||||
useEffect(() => {
|
||||
|
||||
@@ -25,18 +25,41 @@ import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMulti
|
||||
interface IConfGenericProps {
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
fields: KVField[];
|
||||
defaultVals?: IElementValue[];
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) => createStyles({});
|
||||
|
||||
// Function to get defined values,
|
||||
//we make this because the backed sometimes don't return all the keys when there is an initial configuration
|
||||
export const valueDef = (
|
||||
key: string,
|
||||
type: string,
|
||||
defaults: IElementValue[]
|
||||
) => {
|
||||
let defValue = type === "on|off" ? "false" : "";
|
||||
|
||||
if (defaults.length > 0) {
|
||||
const storedConfig = defaults.find(element => element.key === key);
|
||||
|
||||
if (storedConfig) {
|
||||
defValue = storedConfig.value;
|
||||
}
|
||||
}
|
||||
|
||||
return defValue;
|
||||
};
|
||||
|
||||
const ConfTargetGeneric = ({
|
||||
onChange,
|
||||
fields,
|
||||
defaultVals,
|
||||
classes
|
||||
}: IConfGenericProps) => {
|
||||
const [valueHolder, setValueHolder] = useState<IElementValue[]>([]);
|
||||
const fieldsElements = !fields ? [] : fields;
|
||||
const defValList = !defaultVals ? [] : defaultVals;
|
||||
|
||||
// Effect to create all the values to hold
|
||||
useEffect(() => {
|
||||
@@ -44,13 +67,13 @@ const ConfTargetGeneric = ({
|
||||
fields.forEach(field => {
|
||||
const stateInsert: IElementValue = {
|
||||
key: field.name,
|
||||
value: field.type === "on|off" ? "false" : ""
|
||||
value: valueDef(field.name, field.type, defValList)
|
||||
};
|
||||
values.push(stateInsert);
|
||||
});
|
||||
|
||||
setValueHolder(values);
|
||||
}, [fields]);
|
||||
}, [fields, defaultVals]);
|
||||
|
||||
useEffect(() => {
|
||||
onChange(valueHolder);
|
||||
|
||||
@@ -66,7 +66,6 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
configuration_id: "",
|
||||
configuration_label: ""
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
@@ -99,7 +98,6 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
<EditConfiguration
|
||||
open={editScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
setIsLoading(true);
|
||||
setEditScreenOpen(false);
|
||||
}}
|
||||
selectedConfiguration={selectedConfiguration}
|
||||
@@ -141,7 +139,7 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
columns={[
|
||||
{ label: "Configuration", elementKey: "configuration_id" }
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
isLoading={false}
|
||||
records={filteredRecords}
|
||||
entityName="Configurations"
|
||||
idField="configuration_id"
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
@@ -64,9 +65,27 @@ const EditConfiguration = ({
|
||||
//Local States
|
||||
const [valuesObj, setValueObj] = useState<IElementValue[]>([]);
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
|
||||
const [loadingConfig, setLoadingConfig] = useState<boolean>(true);
|
||||
const [errorConfig, setErrorConfig] = useState<string>("");
|
||||
const [configValues, setConfigValues] = useState<IElementValue[]>([]);
|
||||
//Effects
|
||||
useEffect(() => {
|
||||
const configId = get(selectedConfiguration, "configuration_id", false);
|
||||
|
||||
if (configId) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/configs/${configId}`)
|
||||
.then(res => {
|
||||
const keyVals = get(res, "key_values", []);
|
||||
setConfigValues(keyVals);
|
||||
})
|
||||
.catch(err => {
|
||||
setLoadingConfig(false);
|
||||
setErrorConfig(err);
|
||||
});
|
||||
}
|
||||
setLoadingConfig(false);
|
||||
}, [selectedConfiguration]);
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
@@ -81,14 +100,14 @@ const EditConfiguration = ({
|
||||
)
|
||||
.then(res => {
|
||||
setSaving(false);
|
||||
setError("");
|
||||
setErrorConfig("");
|
||||
serverNeedsRestart(true);
|
||||
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch(err => {
|
||||
setSaving(false);
|
||||
setError(err);
|
||||
setErrorConfig(err);
|
||||
});
|
||||
}
|
||||
}, [
|
||||
@@ -119,14 +138,14 @@ const EditConfiguration = ({
|
||||
title={selectedConfiguration.configuration_label}
|
||||
>
|
||||
<React.Fragment>
|
||||
{addError !== "" && (
|
||||
{errorConfig !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
{errorConfig}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
@@ -136,6 +155,7 @@ const EditConfiguration = ({
|
||||
fieldsConfigurations[selectedConfiguration.configuration_id]
|
||||
}
|
||||
onChange={onValueChange}
|
||||
defaultVals={configValues}
|
||||
/>
|
||||
<Grid item xs={3} className={classes.buttonContainer}>
|
||||
<Button
|
||||
@@ -148,6 +168,11 @@ const EditConfiguration = ({
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{loadingConfig && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={9} />
|
||||
</form>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -53,13 +53,11 @@ export const configurationElements: IConfigurationElement[] = [
|
||||
{ configuration_id: "kms_kes", configuration_label: "KMS KES Configuration" },
|
||||
{
|
||||
configuration_id: "logger_webhook",
|
||||
configuration_label: "Logger Webhook Configuration",
|
||||
url: "/webhook/logger"
|
||||
configuration_label: "Logger Webhook Configuration"
|
||||
},
|
||||
{
|
||||
configuration_id: "audit_webhook",
|
||||
configuration_label: "Audit Webhook Configuration",
|
||||
url: "/webhook/audit"
|
||||
configuration_label: "Audit Webhook Configuration"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -119,6 +117,20 @@ export const fieldsConfigurations: any = {
|
||||
tooltip: "Minimum number of access before caching an object",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "watermark_low",
|
||||
required: false,
|
||||
label: "Watermark Low",
|
||||
tooltip: "Watermark Low",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "watermark_high",
|
||||
required: false,
|
||||
label: "Watermark High",
|
||||
tooltip: "Watermark High",
|
||||
type: "number"
|
||||
},
|
||||
{
|
||||
name: "comment",
|
||||
required: false,
|
||||
@@ -129,12 +141,6 @@ export const fieldsConfigurations: any = {
|
||||
}
|
||||
],
|
||||
compression: [
|
||||
{
|
||||
name: "minio_compress",
|
||||
required: true,
|
||||
label: "MinIO Compress",
|
||||
type: "on|off"
|
||||
},
|
||||
{
|
||||
name: "extensions",
|
||||
required: false,
|
||||
@@ -199,6 +205,13 @@ export const fieldsConfigurations: any = {
|
||||
}
|
||||
],
|
||||
identity_openid: [
|
||||
{
|
||||
name: "config_url",
|
||||
required: false,
|
||||
label: "Config URL",
|
||||
tooltip: "Config URL for Client ID configuration",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "client_id",
|
||||
required: false,
|
||||
@@ -206,10 +219,17 @@ export const fieldsConfigurations: any = {
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "config_url",
|
||||
name: "claim_name",
|
||||
required: false,
|
||||
label: "Config URL",
|
||||
tooltip: "Config URL for Client ID configuration",
|
||||
label: "Claim Name",
|
||||
tooltip: "Claim Name",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "claim_prefix",
|
||||
required: false,
|
||||
label: "Claim Prefix",
|
||||
tooltip: "Claim Prefix",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
@@ -294,20 +314,31 @@ export const fieldsConfigurations: any = {
|
||||
],
|
||||
policy_opa: [
|
||||
{
|
||||
name: "opa_url",
|
||||
name: "url",
|
||||
required: true,
|
||||
label: "OPA URL",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "auth_token",
|
||||
required: true,
|
||||
label: "Auth Token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "policy_opa",
|
||||
required: true,
|
||||
label: "Policy OPA",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
kms_vault: [],
|
||||
kms_kes: [],
|
||||
logger_webhook: [
|
||||
{
|
||||
name: "name",
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Name",
|
||||
tooltip: "Name of the webhook",
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
@@ -315,20 +346,13 @@ export const fieldsConfigurations: any = {
|
||||
required: true,
|
||||
label: "Auth Token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
audit_webhook: [
|
||||
{
|
||||
name: "name",
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Name",
|
||||
tooltip: "Name of the webhook",
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
@@ -336,12 +360,6 @@ export const fieldsConfigurations: any = {
|
||||
required: true,
|
||||
label: "Auth Token",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "endpoint",
|
||||
required: true,
|
||||
label: "Endpoint",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -62,6 +62,7 @@ import ConfigurationsList from "./Configurations/ConfigurationPanels/Configurati
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
||||
import Trace from "./Trace/Trace";
|
||||
import Logs from "./Logs/Logs";
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
@@ -304,6 +305,7 @@ class Console extends React.Component<
|
||||
<Route exact path="/webhook/logger" component={WebhookPanel} />
|
||||
<Route exact path="/webhook/audit" component={WebhookPanel} />
|
||||
<Route exct path="/trace" component={Trace} />
|
||||
<Route exct path="/logs" component={Logs} />
|
||||
<Route exact path="/">
|
||||
<Redirect to="/dashboard" />
|
||||
</Route>
|
||||
|
||||
314
portal-ui/src/screens/Console/Logs/Logs.tsx
Normal file
314
portal-ui/src/screens/Console/Logs/Logs.tsx
Normal file
@@ -0,0 +1,314 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
import React, { useEffect } from "react";
|
||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import storage from "local-storage-fallback";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import { logMessageReceived, logResetMessages } from "./actions";
|
||||
import { LogMessage } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import Ansi from "ansi-to-react";
|
||||
import { isNull, isNullOrUndefined } from "util";
|
||||
import { wsProtocol } from "../../../utils/wsUtils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
logList: {
|
||||
background: "white",
|
||||
maxHeight: "400px",
|
||||
overflow: "auto",
|
||||
"& ul": {
|
||||
margin: "4px",
|
||||
padding: "0px"
|
||||
},
|
||||
"& ul li": {
|
||||
listStyle: "none",
|
||||
margin: "0px",
|
||||
padding: "0px",
|
||||
borderBottom: "1px solid #dedede"
|
||||
}
|
||||
},
|
||||
tab: {
|
||||
padding: "25px"
|
||||
},
|
||||
ansiblue: {
|
||||
color: "blue"
|
||||
},
|
||||
logerror: {
|
||||
color: "red"
|
||||
},
|
||||
logerror_tab: {
|
||||
color: "red",
|
||||
padding: "25px"
|
||||
},
|
||||
ansidefault: {
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
|
||||
interface ILogs {
|
||||
classes: any;
|
||||
logMessageReceived: typeof logMessageReceived;
|
||||
logResetMessages: typeof logResetMessages;
|
||||
messages: LogMessage[];
|
||||
}
|
||||
|
||||
const Logs = ({
|
||||
classes,
|
||||
logMessageReceived,
|
||||
logResetMessages,
|
||||
messages
|
||||
}: ILogs) => {
|
||||
useEffect(() => {
|
||||
logResetMessages();
|
||||
const url = new URL(window.location.toString());
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const port = isDev ? "9090" : url.port;
|
||||
|
||||
const wsProt = wsProtocol(url.protocol);
|
||||
|
||||
const c = new W3CWebSocket(
|
||||
`${wsProt}://${url.hostname}:${port}/ws/console`
|
||||
);
|
||||
|
||||
let interval: any | null = null;
|
||||
if (c !== null) {
|
||||
c.onopen = () => {
|
||||
console.log("WebSocket Client Connected");
|
||||
c.send("ok");
|
||||
interval = setInterval(() => {
|
||||
c.send("ok");
|
||||
}, 10 * 1000);
|
||||
};
|
||||
c.onmessage = (message: IMessageEvent) => {
|
||||
// console.log(message.data.toString())
|
||||
let m: LogMessage = JSON.parse(message.data.toString());
|
||||
m.time = new Date(m.time.toString());
|
||||
m.key = Math.random();
|
||||
logMessageReceived(m);
|
||||
};
|
||||
c.onclose = () => {
|
||||
clearInterval(interval);
|
||||
console.log("connection closed by server");
|
||||
};
|
||||
return () => {
|
||||
c.close(1000);
|
||||
clearInterval(interval);
|
||||
console.log("closing websockets");
|
||||
};
|
||||
}
|
||||
}, [logMessageReceived]);
|
||||
|
||||
const timeFromdate = (d: Date) => {
|
||||
let h = d.getHours() < 10 ? `0${d.getHours()}` : `${d.getHours()}`;
|
||||
let m = d.getMinutes() < 10 ? `0${d.getMinutes()}` : `${d.getMinutes()}`;
|
||||
let s = d.getSeconds() < 10 ? `0${d.getSeconds()}` : `${d.getSeconds()}`;
|
||||
|
||||
return `${h}:${m}:${s}:${d.getMilliseconds()}`;
|
||||
};
|
||||
|
||||
// replaces a character of a string with other at a given index
|
||||
const replaceWeirdChar = (
|
||||
origString: string,
|
||||
replaceChar: string,
|
||||
index: number
|
||||
) => {
|
||||
let firstPart = origString.substr(0, index);
|
||||
let lastPart = origString.substr(index + 1);
|
||||
|
||||
let newString = firstPart + replaceChar + lastPart;
|
||||
return newString;
|
||||
};
|
||||
|
||||
const colorify = (str: string) => {
|
||||
// matches strings starting like: `[34mEndpoint: [0m`
|
||||
const colorRegex = /(\[[0-9]+m)(.*?)(\[0+m)/g;
|
||||
let matches = colorRegex.exec(str);
|
||||
if (!isNullOrUndefined(matches)) {
|
||||
let start_color = matches[1];
|
||||
let text = matches[2];
|
||||
|
||||
if (start_color === "[34m") {
|
||||
return <span className={classes.ansiblue}>{text}</span>;
|
||||
}
|
||||
if (start_color === "[1m") {
|
||||
return <span className={classes.ansidarkblue}>{text}</span>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderError = (logElement: LogMessage) => {
|
||||
let errorElems = [];
|
||||
if (!isNullOrUndefined(logElement.error)) {
|
||||
if (logElement.api && logElement.api.name) {
|
||||
errorElems.push(
|
||||
<li key={`api-${logElement.key}`}>
|
||||
<span className={classes.logerror}>API: {logElement.api.name}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.time) {
|
||||
errorElems.push(
|
||||
<li key={`time-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
Time: {timeFromdate(logElement.time)}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.deploymentid) {
|
||||
errorElems.push(
|
||||
<li key={`deploytmentid-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
DeploymentID: {logElement.deploymentid}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.requestID) {
|
||||
errorElems.push(
|
||||
<li key={`requestid-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
RequestID: {logElement.requestID}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.remotehost) {
|
||||
errorElems.push(
|
||||
<li key={`remotehost-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
RemoteHost: {logElement.remotehost}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.host) {
|
||||
errorElems.push(
|
||||
<li key={`host-${logElement.key}`}>
|
||||
<span className={classes.logerror}>Host: {logElement.host}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.userAgent) {
|
||||
errorElems.push(
|
||||
<li key={`useragent-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
UserAgent: {logElement.userAgent}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.error.message) {
|
||||
errorElems.push(
|
||||
<li key={`message-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
Error: {logElement.error.message}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
if (logElement.error.source) {
|
||||
// for all sources add padding
|
||||
for (let s in logElement.error.source) {
|
||||
errorElems.push(
|
||||
<li key={`source-${logElement.key}-${s}`}>
|
||||
<span className={classes.logerror_tab}>
|
||||
{logElement.error.source[s]}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorElems;
|
||||
};
|
||||
|
||||
const renderLog = (logElement: LogMessage) => {
|
||||
let logMessage = logElement.ConsoleMsg;
|
||||
// if somehow after the color code starts with unicode 10 = Line feed
|
||||
// delete it
|
||||
const regexInit = /(\[[0-9]+m)/g;
|
||||
let match = regexInit.exec(logMessage);
|
||||
if (match) {
|
||||
if (logMessage.slice(match[0].length).codePointAt(1) == 10) {
|
||||
logMessage = replaceWeirdChar(logMessage, "", match[0].length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Select what to add color and what not to.
|
||||
const colorRegex = /(\[[0-9]+m)(.*?)(\[0+m)/g;
|
||||
let m = colorRegex.exec(logMessage);
|
||||
|
||||
// get substring if there was a match for to split what
|
||||
// is going to be colored and what not, here we add color
|
||||
// only to the first match.
|
||||
let substr = logMessage.slice(colorRegex.lastIndex);
|
||||
substr = substr.replace(regexInit, "");
|
||||
|
||||
// strClean used for corner case when string has unicode 32 for
|
||||
// space instead of normal space.
|
||||
let strClean = logMessage.replace(regexInit, "");
|
||||
// if starts with multiple spaces add padding
|
||||
if (strClean.startsWith(" ") || strClean.codePointAt(1) === 32) {
|
||||
return (
|
||||
<li key={logElement.key}>
|
||||
<span className={classes.tab}>
|
||||
{colorify(logMessage)} {substr}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
} else if (!isNullOrUndefined(logElement.error)) {
|
||||
// list error message and all sources and error elems
|
||||
return renderError(logElement);
|
||||
} else {
|
||||
// for all remaining set default class
|
||||
return (
|
||||
<li key={logElement.key}>
|
||||
<span className={classes.ansidefault}>
|
||||
{colorify(logMessage)} {substr}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Logs</h1>
|
||||
<div className={classes.logList}>
|
||||
<ul>
|
||||
{messages.map(m => {
|
||||
return renderLog(m);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
messages: state.logs.messages
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
logMessageReceived: logMessageReceived,
|
||||
logResetMessages: logResetMessages
|
||||
});
|
||||
|
||||
export default connector(withStyles(styles)(Logs));
|
||||
44
portal-ui/src/screens/Console/Logs/actions.ts
Normal file
44
portal-ui/src/screens/Console/Logs/actions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import { LogMessage } from "./types";
|
||||
|
||||
export const LOG_MESSAGE_RECEIVED = "LOG_MESSAGE_RECEIVED";
|
||||
export const LOG_RESET_MESSAGES = "LOG_RESET_MESSAGES";
|
||||
|
||||
interface LogMessageReceivedAction {
|
||||
type: typeof LOG_MESSAGE_RECEIVED;
|
||||
message: LogMessage;
|
||||
}
|
||||
|
||||
interface LogResetMessagesAction {
|
||||
type: typeof LOG_RESET_MESSAGES;
|
||||
}
|
||||
|
||||
export type LogActionTypes = LogMessageReceivedAction | LogResetMessagesAction;
|
||||
|
||||
export function logMessageReceived(message: LogMessage) {
|
||||
return {
|
||||
type: LOG_MESSAGE_RECEIVED,
|
||||
message: message
|
||||
};
|
||||
}
|
||||
|
||||
export function logResetMessages() {
|
||||
return {
|
||||
type: LOG_RESET_MESSAGES
|
||||
};
|
||||
}
|
||||
50
portal-ui/src/screens/Console/Logs/reducers.ts
Normal file
50
portal-ui/src/screens/Console/Logs/reducers.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
import {
|
||||
LOG_MESSAGE_RECEIVED,
|
||||
LOG_RESET_MESSAGES,
|
||||
LogActionTypes
|
||||
} from "./actions";
|
||||
import { LogMessage } from "./types";
|
||||
|
||||
export interface LogState {
|
||||
messages: LogMessage[];
|
||||
}
|
||||
|
||||
const initialState: LogState = {
|
||||
messages: []
|
||||
};
|
||||
|
||||
export function logReducer(
|
||||
state = initialState,
|
||||
action: LogActionTypes
|
||||
): LogState {
|
||||
switch (action.type) {
|
||||
case LOG_MESSAGE_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, action.message]
|
||||
};
|
||||
case LOG_RESET_MESSAGES:
|
||||
return {
|
||||
...state,
|
||||
messages: []
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
44
portal-ui/src/screens/Console/Logs/types.ts
Normal file
44
portal-ui/src/screens/Console/Logs/types.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
|
||||
export interface logError {
|
||||
message: string;
|
||||
source: string[];
|
||||
}
|
||||
|
||||
export interface logErrorApiArgs {
|
||||
bucket: string;
|
||||
object: string;
|
||||
}
|
||||
|
||||
export interface logErrorApi {
|
||||
name: string;
|
||||
args: logErrorApiArgs;
|
||||
}
|
||||
|
||||
export interface LogMessage {
|
||||
remotehost: string;
|
||||
host: string;
|
||||
requestID: string;
|
||||
userAgent: string;
|
||||
message: string;
|
||||
api: logErrorApi;
|
||||
deploymentid: string;
|
||||
time: Date;
|
||||
error: logError;
|
||||
ConsoleMsg: string;
|
||||
key: number;
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
import React from "react";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import WebAssetIcon from "@material-ui/icons/WebAsset";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { Divider, Typography, withStyles } from "@material-ui/core";
|
||||
@@ -157,6 +158,12 @@ class Menu extends React.Component<MenuProps> {
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Trace" />
|
||||
</ListItem>
|
||||
<ListItem button component={NavLink} to="/logs">
|
||||
<ListItemIcon>
|
||||
<WebAssetIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Console Logs" />
|
||||
</ListItem>
|
||||
<ListItem component={Typography}>Configuration</ListItem>
|
||||
<ListItem button component={NavLink} to="/notification-endpoints">
|
||||
<ListItemIcon>
|
||||
|
||||
@@ -21,7 +21,8 @@ import { connect } from "react-redux";
|
||||
import { traceMessageReceived, traceResetMessages } from "./actions";
|
||||
import { TraceMessage } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { niceBytes, setCookie } from "../../../common/utils";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { wsProtocol } from "../../../utils/wsUtils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -57,14 +58,12 @@ const Trace = ({
|
||||
}: ITrace) => {
|
||||
useEffect(() => {
|
||||
traceResetMessages();
|
||||
const token: string = storage.getItem("token")!;
|
||||
const url = new URL(window.location.toString());
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const port = isDev ? "9090" : url.port;
|
||||
|
||||
setCookie("token", token);
|
||||
|
||||
const c = new W3CWebSocket(`ws://${url.hostname}:${port}/ws/trace`);
|
||||
const wsProt = wsProtocol(url.protocol);
|
||||
const c = new W3CWebSocket(`${wsProt}://${url.hostname}:${port}/ws/trace`);
|
||||
|
||||
let interval: any | null = null;
|
||||
if (c !== null) {
|
||||
|
||||
@@ -195,24 +195,25 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
||||
} = this.state;
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
this.setState({ page: newPage }, () => {
|
||||
this.fetchRecords();
|
||||
});
|
||||
this.setState({ page: newPage });
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
this.setState({ page: 0, rowsPerPage: rPP }, () => {
|
||||
this.fetchRecords();
|
||||
});
|
||||
this.setState({ page: 0, rowsPerPage: rPP });
|
||||
};
|
||||
|
||||
const filteredRecords = records.filter(elementItem =>
|
||||
elementItem.accessKey.includes(filter)
|
||||
);
|
||||
|
||||
const beginRecord = page * rowsPerPage;
|
||||
const endRecords = beginRecord + rowsPerPage;
|
||||
|
||||
const paginatedRecords = filteredRecords.slice(beginRecord, endRecords);
|
||||
|
||||
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const targetD = e.target;
|
||||
const value = targetD.value;
|
||||
@@ -306,7 +307,7 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
||||
)
|
||||
}}
|
||||
onChange={e => {
|
||||
this.setState({ filter: e.target.value });
|
||||
this.setState({ filter: e.target.value, page: 0 });
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -349,13 +350,13 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
||||
onSelect={selectionChanged}
|
||||
selectedItems={checkedUsers}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
records={paginatedRecords}
|
||||
entityName="Users"
|
||||
idField="accessKey"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
count: filteredRecords.length,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, {FC, useEffect} from "react";
|
||||
import {RouteComponentProps} from "react-router";
|
||||
import React, { FC, useEffect } from "react";
|
||||
import { RouteComponentProps } from "react-router";
|
||||
import storage from "local-storage-fallback";
|
||||
import api from "../../common/api";
|
||||
|
||||
const LoginCallback: FC<RouteComponentProps> = ({location}) => {
|
||||
const LoginCallback: FC<RouteComponentProps> = ({ location }) => {
|
||||
useEffect(() => {
|
||||
const code = (location.search.match(/code=([^&]+)/) || [])[1];
|
||||
const state = (location.search.match(/state=([^&]+)/) || [])[1];
|
||||
api
|
||||
.invoke("POST", "/api/v1/login/oauth2/auth", {code, state})
|
||||
.invoke("POST", "/api/v1/login/oauth2/auth", { code, state })
|
||||
.then((res: any) => {
|
||||
if (res && res.sessionId) {
|
||||
// store the jwt token
|
||||
|
||||
@@ -29,13 +29,14 @@ import { userLoggedIn } from "../../actions";
|
||||
import history from "../../history";
|
||||
import api from "../../common/api";
|
||||
import { ILoginDetails } from "./types";
|
||||
import { setCookie } from "../../common/utils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
"@global": {
|
||||
body: {
|
||||
backgroundColor: "#F4F4F4",
|
||||
},
|
||||
backgroundColor: "#F4F4F4"
|
||||
}
|
||||
},
|
||||
paper: {
|
||||
marginTop: theme.spacing(16),
|
||||
@@ -44,48 +45,48 @@ const styles = (theme: Theme) =>
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
width: "800px",
|
||||
margin: "auto",
|
||||
margin: "auto"
|
||||
},
|
||||
avatar: {
|
||||
margin: theme.spacing(1),
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
backgroundColor: theme.palette.secondary.main
|
||||
},
|
||||
form: {
|
||||
width: "100%", // Fix IE 11 issue.
|
||||
marginTop: theme.spacing(3),
|
||||
marginTop: theme.spacing(3)
|
||||
},
|
||||
submit: {
|
||||
margin: theme.spacing(3, 0, 2),
|
||||
margin: theme.spacing(3, 0, 2)
|
||||
},
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
color: "red"
|
||||
},
|
||||
mainContainer: {
|
||||
borderRadius: "3px",
|
||||
borderRadius: "3px"
|
||||
},
|
||||
theOcean: {
|
||||
borderTopLeftRadius: "3px",
|
||||
borderBottomLeftRadius: "3px",
|
||||
background:
|
||||
"transparent linear-gradient(333deg, #281B6F 1%, #271260 13%, #120D53 83%) 0% 0% no-repeat padding-box;",
|
||||
"transparent linear-gradient(333deg, #281B6F 1%, #271260 13%, #120D53 83%) 0% 0% no-repeat padding-box;"
|
||||
},
|
||||
oceanBg: {
|
||||
backgroundImage: "url(/images/BG_Illustration.svg)",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "bottom left",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
width: "100%"
|
||||
},
|
||||
theLogin: {
|
||||
padding: "76px 62px 20px 62px",
|
||||
padding: "76px 62px 20px 62px"
|
||||
},
|
||||
loadingLoginStrategy: {
|
||||
textAlign: "center",
|
||||
},
|
||||
textAlign: "center"
|
||||
}
|
||||
});
|
||||
|
||||
const mapState = (state: SystemState) => ({
|
||||
loggedIn: state.loggedIn,
|
||||
loggedIn: state.loggedIn
|
||||
});
|
||||
|
||||
const connector = connect(mapState, { userLoggedIn });
|
||||
@@ -116,8 +117,8 @@ class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
loading: false,
|
||||
loginStrategy: {
|
||||
loginStrategy: "",
|
||||
redirect: "",
|
||||
},
|
||||
redirect: ""
|
||||
}
|
||||
};
|
||||
|
||||
fetchConfiguration() {
|
||||
@@ -126,12 +127,12 @@ class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
.invoke("GET", "/api/v1/login")
|
||||
.then((loginDetails: ILoginDetails) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
loading: false
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
loginStrategy: loginDetails,
|
||||
error: "",
|
||||
error: ""
|
||||
});
|
||||
})
|
||||
.catch((err: any) => {
|
||||
@@ -153,6 +154,7 @@ class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
|
||||
if (bodyResponse.sessionId) {
|
||||
// store the jwt token
|
||||
setCookie("token", bodyResponse.sessionId);
|
||||
storage.setItem("token", bodyResponse.sessionId);
|
||||
//return res.body.sessionId;
|
||||
} else if (bodyResponse.error) {
|
||||
@@ -166,7 +168,7 @@ class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
// We push to history the new URL.
|
||||
history.push("/dashboard");
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
this.setState({ error: `${err}` });
|
||||
});
|
||||
};
|
||||
|
||||
@@ -18,10 +18,12 @@ import { applyMiddleware, combineReducers, compose, createStore } from "redux";
|
||||
import thunk from "redux-thunk";
|
||||
import { systemReducer } from "./reducer";
|
||||
import { traceReducer } from "./screens/Console/Trace/reducers";
|
||||
import { logReducer } from "./screens/Console/Logs/reducers";
|
||||
|
||||
const globalReducer = combineReducers({
|
||||
system: systemReducer,
|
||||
trace: traceReducer
|
||||
trace: traceReducer,
|
||||
logs: logReducer
|
||||
});
|
||||
|
||||
declare global {
|
||||
|
||||
22
portal-ui/src/utils/wsUtils.ts
Normal file
22
portal-ui/src/utils/wsUtils.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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/>.
|
||||
export const wsProtocol = (protocol: string): string => {
|
||||
let wsProtocol = "ws";
|
||||
if (protocol == "https:") {
|
||||
wsProtocol = "wss";
|
||||
}
|
||||
return wsProtocol;
|
||||
};
|
||||
@@ -17,9 +17,10 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
"jsx": "react",
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2027,6 +2027,11 @@ alphanum-sort@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
|
||||
integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
|
||||
|
||||
anser@^1.4.1:
|
||||
version "1.4.9"
|
||||
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
|
||||
integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA==
|
||||
|
||||
ansi-align@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
|
||||
@@ -2096,6 +2101,14 @@ ansi-styles@^4.1.0:
|
||||
"@types/color-name" "^1.1.1"
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-to-react@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ansi-to-react/-/ansi-to-react-6.0.5.tgz#b41d4e91dbbf847a373368cfa517f337399aef9b"
|
||||
integrity sha512-kVKsflDGTJP6CnKOrgt7YQGnCurcf4q0rapWTNmx5KcIvgz+wHqjUIkfNmLQcJM6ZeHD0epbSOz0QOLg7fbVmg==
|
||||
dependencies:
|
||||
anser "^1.4.1"
|
||||
escape-carriage "^1.3.0"
|
||||
|
||||
ansicolors@~0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
|
||||
@@ -4496,6 +4509,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3:
|
||||
d "^1.0.1"
|
||||
ext "^1.1.2"
|
||||
|
||||
escape-carriage@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-carriage/-/escape-carriage-1.3.0.tgz#71006b2d4da8cb6828686addafcb094239c742f3"
|
||||
integrity sha512-ATWi5MD8QlAGQOeMgI8zTp671BG8aKvAC0M7yenlxU4CRLGO/sKthxVUyjiOFKjHdIo+6dZZUNFgHFeVEaKfGQ==
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
||||
147
restapi/admin_console.go
Normal file
147
restapi/admin_console.go
Normal file
@@ -0,0 +1,147 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
)
|
||||
|
||||
const logTimeFormat string = "15:04:05 MST 01/02/2006"
|
||||
|
||||
// startConsoleLog starts log of the servers
|
||||
// by first setting a websocket reader that will
|
||||
// check for a heartbeat.
|
||||
//
|
||||
// A WaitGroup is used to handle goroutines and to ensure
|
||||
// all finish in the proper order. If any, sendConsoleLogInfo()
|
||||
// or wsReadCheck() returns, trace should end.
|
||||
func startConsoleLog(conn WSConn, client MinioAdmin) (mError error) {
|
||||
// a WaitGroup waits for a collection of goroutines to finish
|
||||
wg := sync.WaitGroup{}
|
||||
// a cancel context is needed to end all goroutines used
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Set number of goroutines to wait. wg.Wait()
|
||||
// waitsuntil counter is zero (all are done)
|
||||
wg.Add(3)
|
||||
// start go routine for reading websocket heartbeat
|
||||
readErr := wsReadCheck(ctx, &wg, conn)
|
||||
// send Stream of Console Log Info to the ws c.connection
|
||||
logCh := sendConsoleLogInfo(ctx, &wg, conn, client)
|
||||
// If wsReadCheck returns it means that it is not possible to check
|
||||
// ws heartbeat anymore so we stop from doing Console Log, cancel context
|
||||
// for all goroutines.
|
||||
go func(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
if err := <-readErr; err != nil {
|
||||
log.Println("error on wsReadCheck:", err)
|
||||
mError = err
|
||||
}
|
||||
// cancel context for all goroutines.
|
||||
cancel()
|
||||
}(&wg)
|
||||
|
||||
// get logCh err on finish
|
||||
if err := <-logCh; err != nil {
|
||||
mError = err
|
||||
}
|
||||
|
||||
// if logCh closes for any reason,
|
||||
// cancel context for all goroutines
|
||||
cancel()
|
||||
// wait all goroutines to finish
|
||||
wg.Wait()
|
||||
return mError
|
||||
}
|
||||
|
||||
// sendlogInfo sends stream of Console Log Info to the ws connection
|
||||
func sendConsoleLogInfo(ctx context.Context, wg *sync.WaitGroup, conn WSConn, client MinioAdmin) <-chan error {
|
||||
// decrements the WaitGroup counter
|
||||
// by one when the function returns
|
||||
defer wg.Done()
|
||||
ch := make(chan error)
|
||||
go func(ch chan<- error) {
|
||||
defer close(ch)
|
||||
|
||||
// TODO: accept parameters as variables
|
||||
// name of node, default = "" (all)
|
||||
node := ""
|
||||
// number of log lines
|
||||
lineCount := 100
|
||||
// type of logs "minio"|"application"|"all" default = "all"
|
||||
logKind := "all"
|
||||
// Start listening on all Console Log activity.
|
||||
logCh := client.getLogs(ctx, node, lineCount, logKind)
|
||||
|
||||
for logInfo := range logCh {
|
||||
if logInfo.Err != nil {
|
||||
log.Println("error on console logs:", logInfo.Err)
|
||||
ch <- logInfo.Err
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize message to be sent
|
||||
bytes, err := json.Marshal(serializeConsoleLogInfo(&logInfo))
|
||||
if err != nil {
|
||||
fmt.Println("error on json.Marshal:", err)
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
log.Println("error writeMessage:", err)
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}(ch)
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func serializeConsoleLogInfo(l *madmin.LogInfo) (logInfo madmin.LogInfo) {
|
||||
logInfo = *l
|
||||
if logInfo.ConsoleMsg != "" {
|
||||
if strings.HasPrefix(logInfo.ConsoleMsg, "\n") {
|
||||
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)
|
||||
}
|
||||
168
restapi/admin_console_test.go
Normal file
168
restapi/admin_console_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioGetLogsMock func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
|
||||
// mock function of listPolicies()
|
||||
func (ac adminClientMock) getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
return minioGetLogsMock(ctx, node, lineCnt, logKind)
|
||||
}
|
||||
|
||||
func TestAdminConsoleLog(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
mockWSConn := mockConn{}
|
||||
wsClientMock := wsClientMock{madmin: adminClient}
|
||||
function := "startConsoleLog()"
|
||||
|
||||
testReceiver := make(chan madmin.LogInfo, 5)
|
||||
textToReceive := "test message"
|
||||
testStreamSize := 5
|
||||
|
||||
// Test-1: Serve Console with no errors until Console finishes sending
|
||||
// define mock function behavior for minio server Console
|
||||
minioGetLogsMock = func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
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
|
||||
}
|
||||
// mock function of conn.ReadMessage(), no error on read
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, nil
|
||||
}
|
||||
writesCount := 1
|
||||
// mock connection WriteMessage() no error
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
// emulate that receiver gets the message written
|
||||
var t madmin.LogInfo
|
||||
_ = json.Unmarshal(data, &t)
|
||||
if writesCount == testStreamSize {
|
||||
// for testing we need to close the receiver channel
|
||||
close(testReceiver)
|
||||
return nil
|
||||
}
|
||||
testReceiver <- t
|
||||
writesCount++
|
||||
return nil
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); 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(messageType int, data []byte) error {
|
||||
return fmt.Errorf("error on write")
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); assert.Error(err) {
|
||||
assert.Equal("error on write", err.Error())
|
||||
}
|
||||
|
||||
// Test-3: error happens while reading, unexpected Close Error should return error.
|
||||
connWriteMessageMock = func(messageType int, data []byte) error {
|
||||
return nil
|
||||
}
|
||||
// mock function of conn.ReadMessage(), returns unexpected Close Error CloseAbnormalClosure
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, &websocket.CloseError{Code: websocket.CloseAbnormalClosure, Text: ""}
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); assert.Error(err) {
|
||||
assert.Equal("websocket: close 1006 (abnormal closure)", err.Error())
|
||||
}
|
||||
|
||||
// Test-4: error happens while reading, expected Close Error NormalClosure
|
||||
// expected Close Error should not return an error, just end Console.
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, &websocket.CloseError{Code: websocket.CloseNormalClosure, Text: ""}
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-5: error happens while reading, expected Close Error CloseGoingAway
|
||||
// expected Close Error should not return an error, just return.
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, &websocket.CloseError{Code: websocket.CloseGoingAway, Text: ""}
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-6: error happens while reading, non Close Error Type should be returned as
|
||||
// error
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, fmt.Errorf("error on read")
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); assert.Error(err) {
|
||||
assert.Equal("error on read", err.Error())
|
||||
}
|
||||
|
||||
// Test-7: error happens on GetLogs Minio, Console should stop
|
||||
// and error shall be returned.
|
||||
minioGetLogsMock = func(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo {
|
||||
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
|
||||
}
|
||||
// mock function of conn.ReadMessage(), no error on read, should stay unless
|
||||
// context is done.
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, nil
|
||||
}
|
||||
if err := startConsoleLog(mockWSConn, wsClientMock.madmin); assert.Error(err) {
|
||||
assert.Equal("error on Console", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -41,21 +41,21 @@ func registerAdminInfoHandlers(api *operations.McsAPI) {
|
||||
|
||||
}
|
||||
|
||||
type UsageInfo struct {
|
||||
type usageInfo struct {
|
||||
Buckets int64
|
||||
Objects int64
|
||||
Usage int64
|
||||
}
|
||||
|
||||
// getAdminInfo invokes admin info and returns a parsed `UsageInfo` structure
|
||||
func getAdminInfo(ctx context.Context, client MinioAdmin) (*UsageInfo, error) {
|
||||
// getAdminInfo invokes admin info and returns a parsed `usageInfo` structure
|
||||
func getAdminInfo(ctx context.Context, client MinioAdmin) (*usageInfo, error) {
|
||||
serverInfo, err := client.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we are trimming uint64 to int64 this will report an incorrect measurement for numbers greater than
|
||||
// 9,223,372,036,854,775,807
|
||||
return &UsageInfo{
|
||||
return &usageInfo{
|
||||
Buckets: int64(serverInfo.Buckets.Count),
|
||||
Objects: int64(serverInfo.Objects.Count),
|
||||
Usage: int64(serverInfo.Usage.Size),
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -50,37 +49,6 @@ type callStats struct {
|
||||
Ttfb string `json:"timeToFirstByte"`
|
||||
}
|
||||
|
||||
// trace serves madmin.ServiceTraceInfo
|
||||
// on a Websocket connection.
|
||||
func (wsc *wsClient) trace() {
|
||||
defer func() {
|
||||
log.Println("trace stopped")
|
||||
// close connection after return
|
||||
wsc.conn.close()
|
||||
}()
|
||||
log.Println("trace started")
|
||||
|
||||
err := startTraceInfo(wsc.conn, wsc.madmin)
|
||||
// Send Connection Close Message indicating the Status Code
|
||||
// see https://tools.ietf.org/html/rfc6455#page-45
|
||||
if err != nil {
|
||||
// If connection exceeded read deadline send Close
|
||||
// Message Policy Violation code since we don't want
|
||||
// to let the receiver figure out the read deadline.
|
||||
// This is a generic code designed if there is a
|
||||
// need to hide specific details about the policy.
|
||||
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, ""))
|
||||
return
|
||||
}
|
||||
// else, internal server error
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, err.Error()))
|
||||
return
|
||||
}
|
||||
// normal closure
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
}
|
||||
|
||||
// startTraceInfo starts trace of the servers
|
||||
// by first setting a websocket reader that will
|
||||
// check for a heartbeat.
|
||||
@@ -115,7 +83,7 @@ func startTraceInfo(conn WSConn, client MinioAdmin) (mError error) {
|
||||
cancel()
|
||||
}(&wg)
|
||||
|
||||
// wait for traceCh to finish
|
||||
// get traceCh error on finish
|
||||
if err := <-traceCh; err != nil {
|
||||
mError = err
|
||||
}
|
||||
|
||||
@@ -81,8 +81,11 @@ type MinioAdmin interface {
|
||||
startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error)
|
||||
stopProfiling(ctx context.Context) (io.ReadCloser, error)
|
||||
serviceTrace(ctx context.Context, allTrace, errTrace bool) <-chan madmin.ServiceTraceInfo
|
||||
getLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan madmin.LogInfo
|
||||
// Service Accounts
|
||||
addServiceAccount(ctx context.Context, policy *iampolicy.Policy) (mauth.Credentials, error)
|
||||
listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error)
|
||||
deleteServiceAccount(ctx context.Context, serviceAccount string) error
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
@@ -203,16 +206,40 @@ func (ac adminClient) serviceTrace(ctx context.Context, allTrace, errTrace bool)
|
||||
return ac.client.ServiceTrace(ctx, allTrace, errTrace)
|
||||
}
|
||||
|
||||
// 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 *iampolicy.Policy) (mauth.Credentials, error) {
|
||||
return ac.client.AddServiceAccount(ctx, policy)
|
||||
}
|
||||
|
||||
// implements madmin.ListServiceAccounts()
|
||||
func (ac adminClient) listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
|
||||
return ac.client.ListServiceAccounts(ctx)
|
||||
}
|
||||
|
||||
// implements madmin.DeleteServiceAccount()
|
||||
func (ac adminClient) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return ac.client.DeleteServiceAccount(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func newMAdminClient(jwt string) (*madmin.AdminClient, error) {
|
||||
claims, err := auth.JWTAuthenticate(jwt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminClient, err := newAdminFromClaims(claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return adminClient, nil
|
||||
}
|
||||
|
||||
// newAdminFromClaims creates a minio admin from Decrypted claims using Assume role credentials
|
||||
func newAdminFromClaims(claims *auth.DecryptedClaims) (*madmin.AdminClient, error) {
|
||||
adminClient, err := madmin.NewWithOptions(getMinIOEndpoint(), &madmin.Options{
|
||||
Creds: credentials.NewStaticV4(claims.AccessKeyID, claims.SecretAccessKey, claims.SessionToken),
|
||||
Secure: getMinIOEndpointIsSecure(),
|
||||
|
||||
@@ -110,7 +110,7 @@ func (c mcS3Client) removeNotificationConfig(arn string, event string, prefix st
|
||||
return c.client.RemoveNotificationConfig(arn, event, prefix, suffix)
|
||||
}
|
||||
|
||||
// Define MCSCredentials interface with all functions to be implemented
|
||||
// MCSCredentials interface with all functions to be implemented
|
||||
// by mock when testing, it should include all needed minioCredentials.Credentials api calls
|
||||
// that are used within this project.
|
||||
type MCSCredentials interface {
|
||||
|
||||
@@ -24,10 +24,19 @@ import (
|
||||
"github.com/minio/minio/pkg/env"
|
||||
)
|
||||
|
||||
// Port mcs default port
|
||||
var Port = "9090"
|
||||
|
||||
// Hostname mcs hostname
|
||||
var Hostname = "localhost"
|
||||
|
||||
// TLSHostname mcs tls hostname
|
||||
var TLSHostname = "localhost"
|
||||
|
||||
// TLSPort mcs tls port
|
||||
var TLSPort = "9443"
|
||||
|
||||
// TLSRedirect mcs tls redirect rule
|
||||
var TLSRedirect = "off"
|
||||
|
||||
func getAccessKey() string {
|
||||
@@ -70,10 +79,14 @@ func getProductionMode() bool {
|
||||
return strings.ToLower(env.Get(McsProductionMode, "on")) == "on"
|
||||
}
|
||||
|
||||
// GetHostname gets mcs hostname set on env variable,
|
||||
// default one or defined on run command
|
||||
func GetHostname() string {
|
||||
return strings.ToLower(env.Get(McsHostname, Hostname))
|
||||
}
|
||||
|
||||
// GetPort gets mcs por set on env variable
|
||||
// or default one
|
||||
func GetPort() int {
|
||||
port, err := strconv.Atoi(env.Get(McsPort, Port))
|
||||
if err != nil {
|
||||
@@ -82,10 +95,14 @@ func GetPort() int {
|
||||
return port
|
||||
}
|
||||
|
||||
// GetSSLHostname gets mcs ssl hostname set on env variable
|
||||
// or default one
|
||||
func GetSSLHostname() string {
|
||||
return strings.ToLower(env.Get(McsTLSHostname, TLSHostname))
|
||||
}
|
||||
|
||||
// GetSSLPort gets mcs ssl port set on env variable
|
||||
// or default one
|
||||
func GetSSLPort() int {
|
||||
port, err := strconv.Atoi(env.Get(McsTLSPort, TLSPort))
|
||||
if err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"github.com/minio/mcs/models"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
assetFS "github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
portalUI "github.com/minio/mcs/portal-ui"
|
||||
|
||||
@@ -167,11 +167,44 @@ func FileServerMiddleware(next http.Handler) http.Handler {
|
||||
case strings.HasPrefix(r.URL.Path, "/api"):
|
||||
next.ServeHTTP(w, r)
|
||||
default:
|
||||
http.FileServer(&assetfs.AssetFS{
|
||||
assets := assetFS.AssetFS{
|
||||
Asset: portalUI.Asset,
|
||||
AssetDir: portalUI.AssetDir,
|
||||
AssetInfo: portalUI.AssetInfo,
|
||||
Prefix: "build"}).ServeHTTP(w, r)
|
||||
Prefix: "build"}
|
||||
wrapHandlerSinglePageApplication(http.FileServer(&assets)).ServeHTTP(w, r)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type notFoundRedirectRespWr struct {
|
||||
http.ResponseWriter // We embed http.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
func (w *notFoundRedirectRespWr) WriteHeader(status int) {
|
||||
w.status = status // Store the status for our own use
|
||||
if status != http.StatusNotFound {
|
||||
w.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *notFoundRedirectRespWr) Write(p []byte) (int, error) {
|
||||
if w.status != http.StatusNotFound {
|
||||
return w.ResponseWriter.Write(p)
|
||||
}
|
||||
return len(p), nil // Lie that we successfully wrote it
|
||||
}
|
||||
|
||||
// wrapHandlerSinglePageApplication handles a http.FileServer returning a 404 and overrides it with index.html
|
||||
func wrapHandlerSinglePageApplication(h http.Handler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
nfrw := ¬FoundRedirectRespWr{ResponseWriter: w}
|
||||
h.ServeHTTP(nfrw, r)
|
||||
if nfrw.status == 404 {
|
||||
log.Printf("Redirecting %s to index.html.", r.RequestURI)
|
||||
http.Redirect(w, r, "/index.html", http.StatusFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,6 +993,41 @@ func init() {
|
||||
}
|
||||
},
|
||||
"/service-accounts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "List User's Service Accounts",
|
||||
"operationId": "ListUserServiceAccounts",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/serviceAccounts"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
@@ -1005,7 +1040,7 @@ func init() {
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/serviceAccount"
|
||||
"$ref": "#/definitions/serviceAccountRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1025,6 +1060,34 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service-accounts/{access_key}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Delete Service Account",
|
||||
"operationId": "DeleteServiceAccount",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "access_key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service/restart": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1883,15 +1946,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"policy": {
|
||||
"type": "string",
|
||||
"title": "policy to be applied to the Service Account if any"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountCreds": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1903,6 +1957,21 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"policy": {
|
||||
"type": "string",
|
||||
"title": "policy to be applied to the Service Account if any"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sessionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3033,6 +3102,41 @@ func init() {
|
||||
}
|
||||
},
|
||||
"/service-accounts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "List User's Service Accounts",
|
||||
"operationId": "ListUserServiceAccounts",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"name": "offset",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/serviceAccounts"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
@@ -3045,7 +3149,7 @@ func init() {
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/serviceAccount"
|
||||
"$ref": "#/definitions/serviceAccountRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -3065,6 +3169,34 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service-accounts/{access_key}": {
|
||||
"delete": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Delete Service Account",
|
||||
"operationId": "DeleteServiceAccount",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "access_key",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service/restart": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -3923,15 +4055,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"policy": {
|
||||
"type": "string",
|
||||
"title": "policy to be applied to the Service Account if any"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountCreds": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3943,6 +4066,21 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccountRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"policy": {
|
||||
"type": "string",
|
||||
"title": "policy to be applied to the Service Account if any"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceAccounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sessionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -105,6 +105,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
|
||||
UserAPIDeleteBucketEventHandler: user_api.DeleteBucketEventHandlerFunc(func(params user_api.DeleteBucketEventParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.DeleteBucketEvent has not yet been implemented")
|
||||
}),
|
||||
UserAPIDeleteServiceAccountHandler: user_api.DeleteServiceAccountHandlerFunc(func(params user_api.DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.DeleteServiceAccount has not yet been implemented")
|
||||
}),
|
||||
AdminAPIGetUserInfoHandler: admin_api.GetUserInfoHandlerFunc(func(params admin_api.GetUserInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.GetUserInfo has not yet been implemented")
|
||||
}),
|
||||
@@ -126,6 +129,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
|
||||
AdminAPIListPoliciesHandler: admin_api.ListPoliciesHandlerFunc(func(params admin_api.ListPoliciesParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ListPolicies has not yet been implemented")
|
||||
}),
|
||||
UserAPIListUserServiceAccountsHandler: user_api.ListUserServiceAccountsHandlerFunc(func(params user_api.ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.ListUserServiceAccounts has not yet been implemented")
|
||||
}),
|
||||
AdminAPIListUsersHandler: admin_api.ListUsersHandlerFunc(func(params admin_api.ListUsersParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ListUsers has not yet been implemented")
|
||||
}),
|
||||
@@ -263,6 +269,8 @@ type McsAPI struct {
|
||||
UserAPIDeleteBucketHandler user_api.DeleteBucketHandler
|
||||
// UserAPIDeleteBucketEventHandler sets the operation handler for the delete bucket event operation
|
||||
UserAPIDeleteBucketEventHandler user_api.DeleteBucketEventHandler
|
||||
// UserAPIDeleteServiceAccountHandler sets the operation handler for the delete service account operation
|
||||
UserAPIDeleteServiceAccountHandler user_api.DeleteServiceAccountHandler
|
||||
// AdminAPIGetUserInfoHandler sets the operation handler for the get user info operation
|
||||
AdminAPIGetUserInfoHandler admin_api.GetUserInfoHandler
|
||||
// AdminAPIGroupInfoHandler sets the operation handler for the group info operation
|
||||
@@ -277,6 +285,8 @@ type McsAPI struct {
|
||||
AdminAPIListGroupsHandler admin_api.ListGroupsHandler
|
||||
// AdminAPIListPoliciesHandler sets the operation handler for the list policies operation
|
||||
AdminAPIListPoliciesHandler admin_api.ListPoliciesHandler
|
||||
// UserAPIListUserServiceAccountsHandler sets the operation handler for the list user service accounts operation
|
||||
UserAPIListUserServiceAccountsHandler user_api.ListUserServiceAccountsHandler
|
||||
// AdminAPIListUsersHandler sets the operation handler for the list users operation
|
||||
AdminAPIListUsersHandler admin_api.ListUsersHandler
|
||||
// UserAPILoginHandler sets the operation handler for the login operation
|
||||
@@ -432,6 +442,9 @@ func (o *McsAPI) Validate() error {
|
||||
if o.UserAPIDeleteBucketEventHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.DeleteBucketEventHandler")
|
||||
}
|
||||
if o.UserAPIDeleteServiceAccountHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.DeleteServiceAccountHandler")
|
||||
}
|
||||
if o.AdminAPIGetUserInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.GetUserInfoHandler")
|
||||
}
|
||||
@@ -453,6 +466,9 @@ func (o *McsAPI) Validate() error {
|
||||
if o.AdminAPIListPoliciesHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ListPoliciesHandler")
|
||||
}
|
||||
if o.UserAPIListUserServiceAccountsHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.ListUserServiceAccountsHandler")
|
||||
}
|
||||
if o.AdminAPIListUsersHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ListUsersHandler")
|
||||
}
|
||||
@@ -669,6 +685,10 @@ func (o *McsAPI) initHandlerCache() {
|
||||
o.handlers["DELETE"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["DELETE"]["/buckets/{bucket_name}/events/{arn}"] = user_api.NewDeleteBucketEvent(o.context, o.UserAPIDeleteBucketEventHandler)
|
||||
if o.handlers["DELETE"] == nil {
|
||||
o.handlers["DELETE"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["DELETE"]["/service-accounts/{access_key}"] = user_api.NewDeleteServiceAccount(o.context, o.UserAPIDeleteServiceAccountHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
@@ -700,6 +720,10 @@ func (o *McsAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/service-accounts"] = user_api.NewListUserServiceAccounts(o.context, o.UserAPIListUserServiceAccountsHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/users"] = admin_api.NewListUsers(o.context, o.AdminAPIListUsersHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
|
||||
@@ -53,7 +53,7 @@ type CreateServiceAccountParams struct {
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.ServiceAccount
|
||||
Body *models.ServiceAccountRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
@@ -67,7 +67,7 @@ func (o *CreateServiceAccountParams) BindRequest(r *http.Request, route *middlew
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.ServiceAccount
|
||||
var body models.ServiceAccountRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body"))
|
||||
|
||||
90
restapi/operations/user_api/delete_service_account.go
Normal file
90
restapi/operations/user_api/delete_service_account.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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/mcs/models"
|
||||
)
|
||||
|
||||
// DeleteServiceAccountHandlerFunc turns a function with the right signature into a delete service account handler
|
||||
type DeleteServiceAccountHandlerFunc func(DeleteServiceAccountParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn DeleteServiceAccountHandlerFunc) Handle(params DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// DeleteServiceAccountHandler interface for that can handle valid delete service account params
|
||||
type DeleteServiceAccountHandler interface {
|
||||
Handle(DeleteServiceAccountParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewDeleteServiceAccount creates a new http.Handler for the delete service account operation
|
||||
func NewDeleteServiceAccount(ctx *middleware.Context, handler DeleteServiceAccountHandler) *DeleteServiceAccount {
|
||||
return &DeleteServiceAccount{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*DeleteServiceAccount swagger:route DELETE /service-accounts/{access_key} UserAPI deleteServiceAccount
|
||||
|
||||
Delete Service Account
|
||||
|
||||
*/
|
||||
type DeleteServiceAccount struct {
|
||||
Context *middleware.Context
|
||||
Handler DeleteServiceAccountHandler
|
||||
}
|
||||
|
||||
func (o *DeleteServiceAccount) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewDeleteServiceAccountParams()
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// NewDeleteServiceAccountParams creates a new DeleteServiceAccountParams object
|
||||
// no default values defined in spec.
|
||||
func NewDeleteServiceAccountParams() DeleteServiceAccountParams {
|
||||
|
||||
return DeleteServiceAccountParams{}
|
||||
}
|
||||
|
||||
// DeleteServiceAccountParams contains all the bound params for the delete service account operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters DeleteServiceAccount
|
||||
type DeleteServiceAccountParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
AccessKey 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 NewDeleteServiceAccountParams() beforehand.
|
||||
func (o *DeleteServiceAccountParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
rAccessKey, rhkAccessKey, _ := route.Params.GetOK("access_key")
|
||||
if err := o.bindAccessKey(rAccessKey, rhkAccessKey, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindAccessKey binds and validates parameter AccessKey from path.
|
||||
func (o *DeleteServiceAccountParams) bindAccessKey(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.AccessKey = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
113
restapi/operations/user_api/delete_service_account_responses.go
Normal file
113
restapi/operations/user_api/delete_service_account_responses.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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/mcs/models"
|
||||
)
|
||||
|
||||
// DeleteServiceAccountNoContentCode is the HTTP code returned for type DeleteServiceAccountNoContent
|
||||
const DeleteServiceAccountNoContentCode int = 204
|
||||
|
||||
/*DeleteServiceAccountNoContent A successful response.
|
||||
|
||||
swagger:response deleteServiceAccountNoContent
|
||||
*/
|
||||
type DeleteServiceAccountNoContent struct {
|
||||
}
|
||||
|
||||
// NewDeleteServiceAccountNoContent creates DeleteServiceAccountNoContent with default headers values
|
||||
func NewDeleteServiceAccountNoContent() *DeleteServiceAccountNoContent {
|
||||
|
||||
return &DeleteServiceAccountNoContent{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *DeleteServiceAccountNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(204)
|
||||
}
|
||||
|
||||
/*DeleteServiceAccountDefault Generic error response.
|
||||
|
||||
swagger:response deleteServiceAccountDefault
|
||||
*/
|
||||
type DeleteServiceAccountDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewDeleteServiceAccountDefault creates DeleteServiceAccountDefault with default headers values
|
||||
func NewDeleteServiceAccountDefault(code int) *DeleteServiceAccountDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &DeleteServiceAccountDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the delete service account default response
|
||||
func (o *DeleteServiceAccountDefault) WithStatusCode(code int) *DeleteServiceAccountDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the delete service account default response
|
||||
func (o *DeleteServiceAccountDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the delete service account default response
|
||||
func (o *DeleteServiceAccountDefault) WithPayload(payload *models.Error) *DeleteServiceAccountDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the delete service account default response
|
||||
func (o *DeleteServiceAccountDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *DeleteServiceAccountDefault) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
116
restapi/operations/user_api/delete_service_account_urlbuilder.go
Normal file
116
restapi/operations/user_api/delete_service_account_urlbuilder.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
// DeleteServiceAccountURL generates an URL for the delete service account operation
|
||||
type DeleteServiceAccountURL struct {
|
||||
AccessKey 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 *DeleteServiceAccountURL) WithBasePath(bp string) *DeleteServiceAccountURL {
|
||||
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 *DeleteServiceAccountURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *DeleteServiceAccountURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/service-accounts/{access_key}"
|
||||
|
||||
accessKey := o.AccessKey
|
||||
if accessKey != "" {
|
||||
_path = strings.Replace(_path, "{access_key}", accessKey, -1)
|
||||
} else {
|
||||
return nil, errors.New("accessKey is required on DeleteServiceAccountURL")
|
||||
}
|
||||
|
||||
_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 *DeleteServiceAccountURL) 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 *DeleteServiceAccountURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *DeleteServiceAccountURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on DeleteServiceAccountURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on DeleteServiceAccountURL")
|
||||
}
|
||||
|
||||
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 *DeleteServiceAccountURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
90
restapi/operations/user_api/list_user_service_accounts.go
Normal file
90
restapi/operations/user_api/list_user_service_accounts.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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/mcs/models"
|
||||
)
|
||||
|
||||
// ListUserServiceAccountsHandlerFunc turns a function with the right signature into a list user service accounts handler
|
||||
type ListUserServiceAccountsHandlerFunc func(ListUserServiceAccountsParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn ListUserServiceAccountsHandlerFunc) Handle(params ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// ListUserServiceAccountsHandler interface for that can handle valid list user service accounts params
|
||||
type ListUserServiceAccountsHandler interface {
|
||||
Handle(ListUserServiceAccountsParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewListUserServiceAccounts creates a new http.Handler for the list user service accounts operation
|
||||
func NewListUserServiceAccounts(ctx *middleware.Context, handler ListUserServiceAccountsHandler) *ListUserServiceAccounts {
|
||||
return &ListUserServiceAccounts{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*ListUserServiceAccounts swagger:route GET /service-accounts UserAPI listUserServiceAccounts
|
||||
|
||||
List User's Service Accounts
|
||||
|
||||
*/
|
||||
type ListUserServiceAccounts struct {
|
||||
Context *middleware.Context
|
||||
Handler ListUserServiceAccountsHandler
|
||||
}
|
||||
|
||||
func (o *ListUserServiceAccounts) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewListUserServiceAccountsParams()
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// NewListUserServiceAccountsParams creates a new ListUserServiceAccountsParams object
|
||||
// no default values defined in spec.
|
||||
func NewListUserServiceAccountsParams() ListUserServiceAccountsParams {
|
||||
|
||||
return ListUserServiceAccountsParams{}
|
||||
}
|
||||
|
||||
// ListUserServiceAccountsParams contains all the bound params for the list user service accounts operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters ListUserServiceAccounts
|
||||
type ListUserServiceAccountsParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Limit *int32
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Offset *int32
|
||||
}
|
||||
|
||||
// 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 NewListUserServiceAccountsParams() beforehand.
|
||||
func (o *ListUserServiceAccountsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
qLimit, qhkLimit, _ := qs.GetOK("limit")
|
||||
if err := o.bindLimit(qLimit, qhkLimit, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qOffset, qhkOffset, _ := qs.GetOK("offset")
|
||||
if err := o.bindOffset(qOffset, qhkOffset, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindLimit binds and validates parameter Limit from query.
|
||||
func (o *ListUserServiceAccountsParams) bindLimit(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt32(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("limit", "query", "int32", raw)
|
||||
}
|
||||
o.Limit = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindOffset binds and validates parameter Offset from query.
|
||||
func (o *ListUserServiceAccountsParams) bindOffset(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertInt32(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("offset", "query", "int32", raw)
|
||||
}
|
||||
o.Offset = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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/mcs/models"
|
||||
)
|
||||
|
||||
// ListUserServiceAccountsOKCode is the HTTP code returned for type ListUserServiceAccountsOK
|
||||
const ListUserServiceAccountsOKCode int = 200
|
||||
|
||||
/*ListUserServiceAccountsOK A successful response.
|
||||
|
||||
swagger:response listUserServiceAccountsOK
|
||||
*/
|
||||
type ListUserServiceAccountsOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload models.ServiceAccounts `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewListUserServiceAccountsOK creates ListUserServiceAccountsOK with default headers values
|
||||
func NewListUserServiceAccountsOK() *ListUserServiceAccountsOK {
|
||||
|
||||
return &ListUserServiceAccountsOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the list user service accounts o k response
|
||||
func (o *ListUserServiceAccountsOK) WithPayload(payload models.ServiceAccounts) *ListUserServiceAccountsOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the list user service accounts o k response
|
||||
func (o *ListUserServiceAccountsOK) SetPayload(payload models.ServiceAccounts) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ListUserServiceAccountsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
payload := o.Payload
|
||||
if payload == nil {
|
||||
// return empty array
|
||||
payload = models.ServiceAccounts{}
|
||||
}
|
||||
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
|
||||
/*ListUserServiceAccountsDefault Generic error response.
|
||||
|
||||
swagger:response listUserServiceAccountsDefault
|
||||
*/
|
||||
type ListUserServiceAccountsDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewListUserServiceAccountsDefault creates ListUserServiceAccountsDefault with default headers values
|
||||
func NewListUserServiceAccountsDefault(code int) *ListUserServiceAccountsDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &ListUserServiceAccountsDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the list user service accounts default response
|
||||
func (o *ListUserServiceAccountsDefault) WithStatusCode(code int) *ListUserServiceAccountsDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the list user service accounts default response
|
||||
func (o *ListUserServiceAccountsDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the list user service accounts default response
|
||||
func (o *ListUserServiceAccountsDefault) WithPayload(payload *models.Error) *ListUserServiceAccountsDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the list user service accounts default response
|
||||
func (o *ListUserServiceAccountsDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ListUserServiceAccountsDefault) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 user_api
|
||||
|
||||
// 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"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ListUserServiceAccountsURL generates an URL for the list user service accounts operation
|
||||
type ListUserServiceAccountsURL struct {
|
||||
Limit *int32
|
||||
Offset *int32
|
||||
|
||||
_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 *ListUserServiceAccountsURL) WithBasePath(bp string) *ListUserServiceAccountsURL {
|
||||
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 *ListUserServiceAccountsURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *ListUserServiceAccountsURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/service-accounts"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var limitQ string
|
||||
if o.Limit != nil {
|
||||
limitQ = swag.FormatInt32(*o.Limit)
|
||||
}
|
||||
if limitQ != "" {
|
||||
qs.Set("limit", limitQ)
|
||||
}
|
||||
|
||||
var offsetQ string
|
||||
if o.Offset != nil {
|
||||
offsetQ = swag.FormatInt32(*o.Offset)
|
||||
}
|
||||
if offsetQ != "" {
|
||||
qs.Set("offset", offsetQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *ListUserServiceAccountsURL) 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 *ListUserServiceAccountsURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *ListUserServiceAccountsURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on ListUserServiceAccountsURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on ListUserServiceAccountsURL")
|
||||
}
|
||||
|
||||
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 *ListUserServiceAccountsURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -31,6 +31,10 @@ import (
|
||||
"github.com/minio/mcs/restapi/operations/user_api"
|
||||
)
|
||||
|
||||
var (
|
||||
errorGeneric = errors.New("an error occurred, please try again")
|
||||
)
|
||||
|
||||
func registerLoginHandlers(api *operations.McsAPI) {
|
||||
// get login strategy
|
||||
api.UserAPILoginDetailHandler = user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
|
||||
@@ -74,9 +78,32 @@ func login(credentials MCSCredentials) (*string, error) {
|
||||
return &jwt, nil
|
||||
}
|
||||
|
||||
func getConfiguredRegion(client MinioAdmin) string {
|
||||
location := ""
|
||||
configuration, err := getConfig(client, "region")
|
||||
if err != nil {
|
||||
log.Println("error obtaining MinIO region:", err)
|
||||
return location
|
||||
}
|
||||
// region is an array of 1 element
|
||||
if len(configuration) > 0 {
|
||||
location = configuration[0].Value
|
||||
}
|
||||
return location
|
||||
}
|
||||
|
||||
// getLoginResponse performs login() and serializes it to the handler's output
|
||||
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
creds, err := newMcsCredentials(*lr.AccessKey, *lr.SecretKey, "")
|
||||
mAdmin, err := newSuperMAdminClient()
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, errorGeneric
|
||||
}
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
// obtain the configured MinIO region
|
||||
// need it for user authentication
|
||||
location := getConfiguredRegion(adminClient)
|
||||
creds, err := newMcsCredentials(*lr.AccessKey, *lr.SecretKey, location)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, err
|
||||
@@ -131,27 +158,32 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Println("error getting new oauth2 client:", err)
|
||||
return nil, errorGeneric
|
||||
}
|
||||
// initialize new identity provider
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
// Validate user against IDP
|
||||
identity, err := loginOauth2Auth(ctx, identityProvider, *lr.Code, *lr.State)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Println("error validating user identity against idp:", err)
|
||||
return nil, errorGeneric
|
||||
}
|
||||
mAdmin, err := newSuperMAdminClient()
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, errorGeneric
|
||||
}
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
accessKey := identity.Email
|
||||
secretKey := utils.RandomCharString(32)
|
||||
// Create user in MinIO
|
||||
// obtain the configured MinIO region
|
||||
// need it for user authentication
|
||||
location := getConfiguredRegion(adminClient)
|
||||
// create user in MinIO
|
||||
if _, err := addUser(ctx, adminClient, &accessKey, &secretKey, []string{}); err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return nil, err
|
||||
return nil, errorGeneric
|
||||
}
|
||||
// rollback user if there's an error after this point
|
||||
defer func() {
|
||||
@@ -164,19 +196,19 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
// assign the "mcsAdmin" policy to this user
|
||||
if err := setPolicy(ctx, adminClient, oauth2.GetIDPPolicyForUser(), accessKey, models.PolicyEntityUser); err != nil {
|
||||
log.Println("error setting policy:", err)
|
||||
return nil, err
|
||||
return nil, errorGeneric
|
||||
}
|
||||
// User was created correctly, create a new session/JWT
|
||||
creds, err := newMcsCredentials(accessKey, secretKey, "")
|
||||
creds, err := newMcsCredentials(accessKey, secretKey, location)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, err
|
||||
return nil, errorGeneric
|
||||
}
|
||||
credentials := mcsCredentials{minioCredentials: creds}
|
||||
jwt, err := login(credentials)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, err
|
||||
return nil, errorGeneric
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -184,5 +216,5 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
return nil, errors.New("an error occurred, please try again")
|
||||
return nil, errorGeneric
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/mcs/pkg/auth/idp/oauth2"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -101,3 +103,95 @@ func TestLoginOauth2Auth(t *testing.T) {
|
||||
funcAssert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getConfiguredRegion(t *testing.T) {
|
||||
client := adminClientMock{}
|
||||
type args struct {
|
||||
client adminClientMock
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
mock func()
|
||||
}{
|
||||
// If MinIO returns an error, we return empty region name
|
||||
{
|
||||
name: "region",
|
||||
args: args{
|
||||
client: client,
|
||||
},
|
||||
want: "",
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
return nil, errors.New("Invalid config")
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return madmin.Help{}, errors.New("no help")
|
||||
}
|
||||
},
|
||||
},
|
||||
// MinIO returns an empty region name
|
||||
{
|
||||
name: "region",
|
||||
args: args{
|
||||
client: client,
|
||||
},
|
||||
want: "",
|
||||
mock: func() {
|
||||
// mock function response from getConfig()
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
return []byte("region name= "), nil
|
||||
}
|
||||
// mock function response from listConfig()
|
||||
minioHelpConfigKVMock = func(subSys, key string, envOnly bool) (madmin.Help, error) {
|
||||
return madmin.Help{
|
||||
SubSys: config.RegionSubSys,
|
||||
Description: "label the location of the server",
|
||||
MultipleTargets: false,
|
||||
KeysHelp: []madmin.HelpKV{
|
||||
{
|
||||
Key: "name",
|
||||
Description: "name of the location of the server e.g. \"us-west-rack2\"",
|
||||
Optional: true,
|
||||
Type: "string",
|
||||
MultipleTargets: false,
|
||||
},
|
||||
{
|
||||
Key: "comment",
|
||||
Description: "optionally add a comment to this setting",
|
||||
Optional: true,
|
||||
Type: "sentence",
|
||||
MultipleTargets: false,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
// MinIO returns the asia region
|
||||
{
|
||||
name: "region",
|
||||
args: args{
|
||||
client: client,
|
||||
},
|
||||
want: "asia",
|
||||
mock: func() {
|
||||
minioGetConfigKVMock = func(key string) ([]byte, error) {
|
||||
return []byte("region name=asia "), nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt.mock()
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getConfiguredRegion(tt.args.client); got != tt.want {
|
||||
t.Errorf("getConfiguredRegion() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,24 @@ func registerServiceAccountsHandlers(api *operations.McsAPI) {
|
||||
}
|
||||
return user_api.NewCreateServiceAccountCreated().WithPayload(creds)
|
||||
})
|
||||
// List Service Accounts for User
|
||||
api.UserAPIListUserServiceAccountsHandler = user_api.ListUserServiceAccountsHandlerFunc(func(params user_api.ListUserServiceAccountsParams, principal *models.Principal) middleware.Responder {
|
||||
sessionID := string(*principal)
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(sessionID)
|
||||
if err != nil {
|
||||
return user_api.NewListUserServiceAccountsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
}
|
||||
return user_api.NewListUserServiceAccountsOK().WithPayload(serviceAccounts)
|
||||
})
|
||||
|
||||
// Delete a User's service account
|
||||
api.UserAPIDeleteServiceAccountHandler = user_api.DeleteServiceAccountHandlerFunc(func(params user_api.DeleteServiceAccountParams, principal *models.Principal) middleware.Responder {
|
||||
sessionID := string(*principal)
|
||||
if err := getDeleteServiceAccountResponse(sessionID, params.AccessKey); err != nil {
|
||||
return user_api.NewDeleteServiceAccountDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
}
|
||||
return user_api.NewDeleteServiceAccountNoContent()
|
||||
})
|
||||
}
|
||||
|
||||
// createServiceAccount adds a service account to the userClient and assigns a policy to him if defined.
|
||||
@@ -64,7 +82,7 @@ func createServiceAccount(ctx context.Context, userClient MinioAdmin, policy str
|
||||
// getCreateServiceAccountResponse creates a service account with the defined policy for the user that
|
||||
// is requestingit ,it first gets the credentials of the user and creates a client which is going to
|
||||
// make the call to create the Service Account
|
||||
func getCreateServiceAccountResponse(userSessionID string, serviceAccount *models.ServiceAccount) (*models.ServiceAccountCreds, error) {
|
||||
func getCreateServiceAccountResponse(userSessionID string, serviceAccount *models.ServiceAccountRequest) (*models.ServiceAccountCreds, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
@@ -84,3 +102,66 @@ func getCreateServiceAccountResponse(userSessionID string, serviceAccount *model
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
|
||||
// getUserServiceAccount gets list of the user's service accounts
|
||||
func getUserServiceAccounts(ctx context.Context, userClient MinioAdmin) (models.ServiceAccounts, error) {
|
||||
listServAccs, err := userClient.listServiceAccounts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAccounts := models.ServiceAccounts{}
|
||||
for _, acc := range listServAccs.Accounts {
|
||||
serviceAccounts = append(serviceAccounts, acc)
|
||||
}
|
||||
return serviceAccounts, nil
|
||||
}
|
||||
|
||||
// getUserServiceAccountsResponse authenticates the user and calls
|
||||
// getUserServiceAccounts to list the user's service accounts
|
||||
func getUserServiceAccountsResponse(userSessionID string) (models.ServiceAccounts, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := newMAdminClient(userSessionID)
|
||||
if err != nil {
|
||||
log.Println("error creating user Client:", err)
|
||||
return nil, err
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := adminClient{client: userAdmin}
|
||||
|
||||
serviceAccounts, err := getUserServiceAccounts(ctx, userAdminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing user's service account:", err)
|
||||
return nil, err
|
||||
}
|
||||
return serviceAccounts, nil
|
||||
|
||||
}
|
||||
|
||||
// deleteServiceAccount calls delete service account api
|
||||
func deleteServiceAccount(ctx context.Context, userClient MinioAdmin, accessKey string) error {
|
||||
return userClient.deleteServiceAccount(ctx, accessKey)
|
||||
}
|
||||
|
||||
// getDeleteServiceAccountResponse authenticates the user and calls deleteServiceAccount
|
||||
func getDeleteServiceAccountResponse(userSessionID, accessKey string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := newMAdminClient(userSessionID)
|
||||
if err != nil {
|
||||
log.Println("error creating user Client:", err)
|
||||
return err
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := adminClient{client: userAdmin}
|
||||
|
||||
if err := deleteServiceAccount(ctx, userAdminClient, accessKey); err != nil {
|
||||
log.Println("error deleting user's service account:", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,17 +25,30 @@ import (
|
||||
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// assigning mock at runtime instead of compile time
|
||||
var minioAddServiceAccountMock func(ctx context.Context, policy *iampolicy.Policy) (auth.Credentials, error)
|
||||
var minioListServiceAccountsMock func(ctx context.Context) (madmin.ListServiceAccountsResp, error)
|
||||
var minioDeleteServiceAccountMock func(ctx context.Context, serviceAccount string) error
|
||||
|
||||
// mock function of listUsers()
|
||||
// mock function of AddServiceAccount()
|
||||
func (ac adminClientMock) addServiceAccount(ctx context.Context, policy *iampolicy.Policy) (auth.Credentials, error) {
|
||||
return minioAddServiceAccountMock(ctx, policy)
|
||||
}
|
||||
|
||||
// mock function of ListServiceAccounts()
|
||||
func (ac adminClientMock) listServiceAccounts(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
|
||||
return minioListServiceAccountsMock(ctx)
|
||||
}
|
||||
|
||||
// mock function of DeleteServiceAccount()
|
||||
func (ac adminClientMock) deleteServiceAccount(ctx context.Context, serviceAccount string) error {
|
||||
return minioDeleteServiceAccountMock(ctx, serviceAccount)
|
||||
}
|
||||
|
||||
func TestAddServiceAccount(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
@@ -84,3 +97,61 @@ func TestAddServiceAccount(t *testing.T) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestListServiceAccounts(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
client := adminClientMock{}
|
||||
function := "getUserServiceAccounts()"
|
||||
|
||||
// Test-1: getUserServiceAccounts list serviceaccounts for a user
|
||||
ctx := context.Background()
|
||||
mockResponse := madmin.ListServiceAccountsResp{
|
||||
Accounts: []string{"accesskey1", "accesskey2"},
|
||||
}
|
||||
minioListServiceAccountsMock = func(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
|
||||
return mockResponse, nil
|
||||
}
|
||||
serviceAccounts, err := getUserServiceAccounts(ctx, client)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
for i, sa := range serviceAccounts {
|
||||
assert.Equal(mockResponse.Accounts[i], sa)
|
||||
}
|
||||
|
||||
// Test-2: getUserServiceAccounts returns an error, handle it properly
|
||||
minioListServiceAccountsMock = func(ctx context.Context) (madmin.ListServiceAccountsResp, error) {
|
||||
return madmin.ListServiceAccountsResp{}, errors.New("error")
|
||||
}
|
||||
_, err = getUserServiceAccounts(ctx, client)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteServiceAccount(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// mock minIO client
|
||||
client := adminClientMock{}
|
||||
function := "deleteServiceAccount()"
|
||||
ctx := context.Background()
|
||||
|
||||
// Test-1: deleteServiceAccount receive a service account to delete
|
||||
testServiceAccount := "accesskeytest"
|
||||
minioDeleteServiceAccountMock = func(ctx context.Context, serviceAccount string) error {
|
||||
return nil
|
||||
}
|
||||
if err := deleteServiceAccount(ctx, client, testServiceAccount); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: if an invalid policy is assigned to the service account, this will raise an error
|
||||
minioDeleteServiceAccountMock = func(ctx context.Context, serviceAccount string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
if err := deleteServiceAccount(ctx, client, testServiceAccount); assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package restapi
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -108,9 +109,8 @@ func (c wsConn) readMessage() (messageType int, p []byte, err error) {
|
||||
//
|
||||
// TODO: Enable CORS
|
||||
func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
// authenticate WS connection
|
||||
// TODO: use this claims to create the adminClient
|
||||
_, err := ws.Authenticate(req)
|
||||
// authenticate WS connection with MCS
|
||||
claims, err := ws.Authenticate(req)
|
||||
if err != nil {
|
||||
log.Print("error on ws authentication: ", err)
|
||||
errors.ServeError(w, req, err)
|
||||
@@ -125,13 +125,9 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: CHANGE ! to use newMAdminClient once Assume Role is
|
||||
// allowed to do Trace use jwt on ws.
|
||||
|
||||
// Using newSuperMAdminClient in the meantime for sake of functionality
|
||||
// Only start Websocket Interaction after user has been
|
||||
// authenticated.
|
||||
mAdmin, err := newSuperMAdminClient()
|
||||
// authenticated with MinIO
|
||||
mAdmin, err := newAdminFromClaims(claims)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
errors.ServeError(w, req, err)
|
||||
@@ -149,6 +145,8 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
switch strings.TrimPrefix(req.URL.Path, wsBasePath) {
|
||||
case "/trace":
|
||||
go wsClient.trace()
|
||||
case "/console":
|
||||
go wsClient.console()
|
||||
default:
|
||||
// path not found
|
||||
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
@@ -205,3 +203,65 @@ func wsReadCheck(ctx context.Context, wg *sync.WaitGroup, conn WSConn) chan erro
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// trace serves madmin.ServiceTraceInfo
|
||||
// on a Websocket connection.
|
||||
func (wsc *wsClient) trace() {
|
||||
defer func() {
|
||||
log.Println("trace stopped")
|
||||
// close connection after return
|
||||
wsc.conn.close()
|
||||
}()
|
||||
log.Println("trace started")
|
||||
|
||||
err := startTraceInfo(wsc.conn, wsc.madmin)
|
||||
// Send Connection Close Message indicating the Status Code
|
||||
// see https://tools.ietf.org/html/rfc6455#page-45
|
||||
if err != nil {
|
||||
// If connection exceeded read deadline send Close
|
||||
// Message Policy Violation code since we don't want
|
||||
// to let the receiver figure out the read deadline.
|
||||
// This is a generic code designed if there is a
|
||||
// need to hide specific details about the policy.
|
||||
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, ""))
|
||||
return
|
||||
}
|
||||
// else, internal server error
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, err.Error()))
|
||||
return
|
||||
}
|
||||
// normal closure
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
}
|
||||
|
||||
// console serves madmin.GetLogs
|
||||
// on a Websocket connection.
|
||||
func (wsc *wsClient) console() {
|
||||
defer func() {
|
||||
log.Println("console logs stopped")
|
||||
// close connection after return
|
||||
wsc.conn.close()
|
||||
}()
|
||||
log.Println("console logs started")
|
||||
|
||||
err := startConsoleLog(wsc.conn, wsc.madmin)
|
||||
// Send Connection Close Message indicating the Status Code
|
||||
// see https://tools.ietf.org/html/rfc6455#page-45
|
||||
if err != nil {
|
||||
// If connection exceeded read deadline send Close
|
||||
// Message Policy Violation code since we don't want
|
||||
// to let the receiver figure out the read deadline.
|
||||
// This is a generic code designed if there is a
|
||||
// need to hide specific details about the policy.
|
||||
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, ""))
|
||||
return
|
||||
}
|
||||
// else, internal server error
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, err.Error()))
|
||||
return
|
||||
}
|
||||
// normal closure
|
||||
wsc.conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
}
|
||||
|
||||
52
swagger.yml
52
swagger.yml
@@ -309,6 +309,31 @@ paths:
|
||||
- UserAPI
|
||||
|
||||
/service-accounts:
|
||||
get:
|
||||
summary: List User's Service Accounts
|
||||
operationId: ListUserServiceAccounts
|
||||
parameters:
|
||||
- name: offset
|
||||
in: query
|
||||
required: false
|
||||
type: integer
|
||||
format: int32
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/serviceAccounts"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
post:
|
||||
summary: Create Service Account
|
||||
operationId: CreateServiceAccount
|
||||
@@ -317,7 +342,7 @@ paths:
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/serviceAccount'
|
||||
$ref: '#/definitions/serviceAccountRequest'
|
||||
responses:
|
||||
201:
|
||||
description: A successful response.
|
||||
@@ -329,6 +354,25 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/service-accounts/{access_key}:
|
||||
delete:
|
||||
summary: Delete Service Account
|
||||
operationId: DeleteServiceAccount
|
||||
parameters:
|
||||
- name: access_key
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
204:
|
||||
description: A successful response.
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/users:
|
||||
get:
|
||||
@@ -1367,7 +1411,11 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
serviceAccount:
|
||||
serviceAccounts:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
serviceAccountRequest:
|
||||
type: object
|
||||
properties:
|
||||
policy:
|
||||
|
||||
Reference in New Issue
Block a user