Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14fe7c1269 | ||
|
|
b6938a5888 | ||
|
|
901358e8d4 | ||
|
|
5155aef802 | ||
|
|
23b3283014 | ||
|
|
53eb59f5ad |
@@ -78,6 +78,10 @@ nfpms:
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
contents:
|
||||
# Basic file that applies to all packagers
|
||||
- src: systemd/console.service
|
||||
dst: /etc/systemd/system/minio-console.service
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
|
||||
@@ -6,8 +6,8 @@ COPY LICENSE /licenses/LICENSE
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="v0.6.4" \
|
||||
release="v0.6.4" \
|
||||
version="v0.6.6" \
|
||||
release="v0.6.6" \
|
||||
summary="A graphical user interface for MinIO" \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/cmd/config"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/minio/cli"
|
||||
@@ -68,6 +72,21 @@ var serverCmd = cli.Command{
|
||||
Value: certs.GlobalCertsCADir.Get(),
|
||||
Usage: "path to certs directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-certificate",
|
||||
Value: "",
|
||||
Usage: "path tls certificate",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-key",
|
||||
Value: "",
|
||||
Usage: "path tls key",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-ca",
|
||||
Value: "",
|
||||
Usage: "path tls ca",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -122,6 +141,29 @@ func startServer(ctx *cli.Context) error {
|
||||
// load the certificates and the CAs
|
||||
restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager = certs.GetAllCertificatesAndCAs()
|
||||
|
||||
// TLS flags from swagger server, used to support older versions of minio-operator
|
||||
swaggerServerCertificate := ctx.String("tls-certificate")
|
||||
swaggerServerCertificateKey := ctx.String("tls-key")
|
||||
SwaggerServerCACertificate := ctx.String("tls-ca")
|
||||
// load tls cert and key from swagger server tls-certificate and tls-key flags
|
||||
if swaggerServerCertificate != "" && swaggerServerCertificateKey != "" {
|
||||
if errAddCert := certs.AddCertificate(context.Background(), restapi.GlobalTLSCertsManager, swaggerServerCertificate, swaggerServerCertificateKey); errAddCert != nil {
|
||||
log.Println(errAddCert)
|
||||
}
|
||||
if x509Certs, errParseCert := config.ParsePublicCertFile(swaggerServerCertificate); errParseCert == nil {
|
||||
if len(x509Certs) > 0 {
|
||||
restapi.GlobalPublicCerts = append(restapi.GlobalPublicCerts, x509Certs[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
// load ca cert from swagger server tls-ca flag
|
||||
if SwaggerServerCACertificate != "" {
|
||||
caCert, caCertErr := ioutil.ReadFile(SwaggerServerCACertificate)
|
||||
if caCertErr == nil {
|
||||
restapi.GlobalRootCAs.AppendCertsFromPEM(caCert)
|
||||
}
|
||||
}
|
||||
|
||||
if len(restapi.GlobalPublicCerts) > 0 {
|
||||
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
|
||||
// plain HTTP connections to HTTPS server
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.6.4
|
||||
image: minio/console:v0.6.6
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.6.4
|
||||
image: minio/console:v0.6.6
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
97
models/has_permission_request.go
Normal file
97
models/has_permission_request.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// HasPermissionRequest has permission request
|
||||
//
|
||||
// swagger:model hasPermissionRequest
|
||||
type HasPermissionRequest struct {
|
||||
|
||||
// actions
|
||||
Actions []*PolicyArgs `json:"actions"`
|
||||
}
|
||||
|
||||
// Validate validates this has permission request
|
||||
func (m *HasPermissionRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateActions(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HasPermissionRequest) validateActions(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Actions) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Actions); i++ {
|
||||
if swag.IsZero(m.Actions[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Actions[i] != nil {
|
||||
if err := m.Actions[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("actions" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *HasPermissionRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *HasPermissionRequest) UnmarshalBinary(b []byte) error {
|
||||
var res HasPermissionRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
97
models/has_permission_response.go
Normal file
97
models/has_permission_response.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// HasPermissionResponse has permission response
|
||||
//
|
||||
// swagger:model hasPermissionResponse
|
||||
type HasPermissionResponse struct {
|
||||
|
||||
// permissions
|
||||
Permissions []*PermissionAction `json:"permissions"`
|
||||
}
|
||||
|
||||
// Validate validates this has permission response
|
||||
func (m *HasPermissionResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validatePermissions(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HasPermissionResponse) validatePermissions(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Permissions) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Permissions); i++ {
|
||||
if swag.IsZero(m.Permissions[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Permissions[i] != nil {
|
||||
if err := m.Permissions[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("permissions" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *HasPermissionResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *HasPermissionResponse) UnmarshalBinary(b []byte) error {
|
||||
var res HasPermissionResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
63
models/permission_action.go
Normal file
63
models/permission_action.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// PermissionAction permission action
|
||||
//
|
||||
// swagger:model permissionAction
|
||||
type PermissionAction struct {
|
||||
|
||||
// can
|
||||
Can bool `json:"can,omitempty"`
|
||||
|
||||
// id
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this permission action
|
||||
func (m *PermissionAction) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *PermissionAction) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *PermissionAction) UnmarshalBinary(b []byte) error {
|
||||
var res PermissionAction
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
66
models/policy_args.go
Normal file
66
models/policy_args.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// PolicyArgs policy args
|
||||
//
|
||||
// swagger:model policyArgs
|
||||
type PolicyArgs struct {
|
||||
|
||||
// action
|
||||
Action string `json:"action,omitempty"`
|
||||
|
||||
// bucket name
|
||||
BucketName string `json:"bucket_name,omitempty"`
|
||||
|
||||
// id
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this policy args
|
||||
func (m *PolicyArgs) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *PolicyArgs) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *PolicyArgs) UnmarshalBinary(b []byte) error {
|
||||
var res PolicyArgs
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -228,3 +228,14 @@ func GetAllCertificatesAndCAs() (*x509.CertPool, []*x509.Certificate, *xcerts.Ma
|
||||
logger.FatalIf(err, "Unable to load the TLS configuration")
|
||||
return GlobalRootCAs, globalPublicCerts, globalTLSCertsManager
|
||||
}
|
||||
|
||||
// AddCertificate check if Manager is initialized and then append a new certificate to it
|
||||
func AddCertificate(ctx context.Context, manager *xcerts.Manager, publicKey, privateKey string) (err error) {
|
||||
// If Cert Manager is not nil add more certificates
|
||||
if manager != nil {
|
||||
return manager.AddCertificate(publicKey, privateKey)
|
||||
}
|
||||
// Initialize cert manager
|
||||
manager, err = xcerts.NewManager(ctx, publicKey, privateKey, config.LoadX509KeyPair)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.a19f3d53.chunk.css",
|
||||
"main.js": "/static/js/main.1ecd68ff.chunk.js",
|
||||
"main.js.map": "/static/js/main.1ecd68ff.chunk.js.map",
|
||||
"main.js": "/static/js/main.fa0873e1.chunk.js",
|
||||
"main.js.map": "/static/js/main.fa0873e1.chunk.js.map",
|
||||
"runtime-main.js": "/static/js/runtime-main.f48e99e5.js",
|
||||
"runtime-main.js.map": "/static/js/runtime-main.f48e99e5.js.map",
|
||||
"static/css/2.76b14b73.chunk.css": "/static/css/2.76b14b73.chunk.css",
|
||||
"static/js/2.41957633.chunk.js": "/static/js/2.41957633.chunk.js",
|
||||
"static/js/2.41957633.chunk.js.map": "/static/js/2.41957633.chunk.js.map",
|
||||
"static/css/2.f324abd6.chunk.css": "/static/css/2.f324abd6.chunk.css",
|
||||
"static/js/2.44b7c49b.chunk.js": "/static/js/2.44b7c49b.chunk.js",
|
||||
"static/js/2.44b7c49b.chunk.js.map": "/static/js/2.44b7c49b.chunk.js.map",
|
||||
"index.html": "/index.html",
|
||||
"static/css/2.76b14b73.chunk.css.map": "/static/css/2.76b14b73.chunk.css.map",
|
||||
"static/css/2.f324abd6.chunk.css.map": "/static/css/2.f324abd6.chunk.css.map",
|
||||
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
|
||||
"static/js/2.41957633.chunk.js.LICENSE.txt": "/static/js/2.41957633.chunk.js.LICENSE.txt",
|
||||
"static/js/2.44b7c49b.chunk.js.LICENSE.txt": "/static/js/2.44b7c49b.chunk.js.LICENSE.txt",
|
||||
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
|
||||
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.f48e99e5.js",
|
||||
"static/css/2.76b14b73.chunk.css",
|
||||
"static/js/2.41957633.chunk.js",
|
||||
"static/css/2.f324abd6.chunk.css",
|
||||
"static/js/2.44b7c49b.chunk.js",
|
||||
"static/css/main.a19f3d53.chunk.css",
|
||||
"static/js/main.1ecd68ff.chunk.js"
|
||||
"static/js/main.fa0873e1.chunk.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.76b14b73.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.41957633.chunk.js"></script><script src="/static/js/main.1ecd68ff.chunk.js"></script></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.f324abd6.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.44b7c49b.chunk.js"></script><script src="/static/js/main.fa0873e1.chunk.js"></script></body></html>
|
||||
@@ -1,2 +1,2 @@
|
||||
.ReactVirtualized__Table__headerRow{font-weight:700;text-transform:uppercase}.ReactVirtualized__Table__headerRow,.ReactVirtualized__Table__row{display:flex;flex-direction:row;align-items:center}.ReactVirtualized__Table__headerTruncatedText{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ReactVirtualized__Table__headerColumn,.ReactVirtualized__Table__rowColumn{margin-right:10px;min-width:0}.ReactVirtualized__Table__rowColumn{text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn:first-of-type,.ReactVirtualized__Table__rowColumn:first-of-type{margin-left:10px}.ReactVirtualized__Table__sortableHeaderColumn{cursor:pointer}.ReactVirtualized__Table__sortableHeaderIconContainer{display:flex;align-items:center}.ReactVirtualized__Table__sortableHeaderIcon{flex:0 0 24px;height:1em;width:1em;fill:currentColor}.react-grid-layout{position:relative;transition:height .2s ease}.react-grid-item{transition:all .2s ease;transition-property:left,top}.react-grid-item img{pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.react-grid-item.cssTransforms{transition-property:transform}.react-grid-item.resizing{z-index:1;will-change:width,height}.react-grid-item.react-draggable-dragging{transition:none;z-index:3;will-change:transform}.react-grid-item.dropping{visibility:hidden}.react-grid-item.react-grid-placeholder{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.react-grid-item>.react-resizable-handle{position:absolute;width:20px;height:20px}.react-grid-item>.react-resizable-handle:after{content:"";position:absolute;right:3px;bottom:3px;width:5px;height:5px;border-right:2px solid rgba(0,0,0,.4);border-bottom:2px solid rgba(0,0,0,.4)}.react-resizable-hide>.react-resizable-handle{display:none}.react-grid-item>.react-resizable-handle.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e,.react-grid-item>.react-resizable-handle.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-n,.react-grid-item>.react-resizable-handle.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}.react-resizable{position:relative}.react-resizable-handle{position:absolute;width:20px;height:20px;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgd2lkdGg9IjYiIGhlaWdodD0iNiI+PHBhdGggZD0iTTYgNkgwVjQuMmg0LjJWMEg2djZ6IiBvcGFjaXR5PSIuMzAyIi8+PC9zdmc+");background-position:100% 100%;padding:0 3px 3px 0}.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-resizable-handle-e,.react-resizable-handle-w{top:50%;margin-top:-10px;cursor:ew-resize}.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-resizable-handle-n,.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}
|
||||
/*# sourceMappingURL=2.76b14b73.chunk.css.map */
|
||||
/*# sourceMappingURL=2.f324abd6.chunk.css.map */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/main.fa0873e1.chunk.js
Normal file
2
portal-ui/build/static/js/main.fa0873e1.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/main.fa0873e1.chunk.js.map
Normal file
1
portal-ui/build/static/js/main.fa0873e1.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
@@ -23,7 +23,7 @@ import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import Moment from "react-moment";
|
||||
import { Bucket, BucketList } from "../types";
|
||||
import { Bucket, BucketList, HasPermissionResponse } from "../types";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { AppState } from "../../../../store";
|
||||
@@ -92,6 +92,43 @@ const ListBuckets = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
|
||||
const [canCreateBucket, setCanCreateBucket] = useState<boolean>(false);
|
||||
|
||||
// check the permissions for creating bucket
|
||||
useEffect(() => {
|
||||
if (loadingPerms) {
|
||||
api
|
||||
.invoke("POST", `/api/v1/has-permission`, {
|
||||
actions: [
|
||||
{
|
||||
id: "createBucket",
|
||||
action: "s3:CreateBucket",
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((res: HasPermissionResponse) => {
|
||||
setLoadingPerms(false);
|
||||
if (!res.permissions) {
|
||||
return;
|
||||
}
|
||||
const actions = res.permissions ? res.permissions : [];
|
||||
|
||||
let canCreate = actions.find((s) => s.id == "createBucket");
|
||||
if (canCreate && canCreate.can) {
|
||||
setCanCreateBucket(true);
|
||||
} else {
|
||||
setCanCreateBucket(false);
|
||||
}
|
||||
|
||||
setLoadingPerms(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoadingPerms(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loadingPerms, setErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
@@ -155,7 +192,7 @@ const ListBuckets = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{addBucketModalOpen && (
|
||||
<AddBucket
|
||||
open={addBucketModalOpen}
|
||||
@@ -192,16 +229,18 @@ const ListBuckets = ({
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
addBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
{canCreateBucket && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
addBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
@@ -232,7 +271,7 @@ const ListBuckets = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, IconButton } from "@material-ui/core";
|
||||
@@ -38,12 +38,12 @@ import {
|
||||
BucketReplicationRule,
|
||||
BucketReplicationRuleDeleteMarker,
|
||||
BucketVersioning,
|
||||
HasPermissionResponse,
|
||||
} from "../types";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import SetAccessPolicy from "./SetAccessPolicy";
|
||||
import SetRetentionConfig from "./SetRetentionConfig";
|
||||
import AddEvent from "./AddEvent";
|
||||
import DeleteEvent from "./DeleteEvent";
|
||||
@@ -55,6 +55,9 @@ import PencilIcon from "../../Common/TableWrapper/TableActionIcons/PencilIcon";
|
||||
import EnableVersioningModal from "./EnableVersioningModal";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import UsageIcon from "../../../../icons/UsageIcon";
|
||||
import AddPolicy from "../../Policies/AddPolicy";
|
||||
import SetAccessPolicy from "./SetAccessPolicy";
|
||||
import { Policy } from "../../Policies/types";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -66,7 +69,6 @@ const styles = (theme: Theme) =>
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
@@ -196,7 +198,7 @@ function TabPanel(props: TabPanelProps) {
|
||||
style={{ marginTop: "5px" }}
|
||||
{...other}
|
||||
>
|
||||
{value === index && <React.Fragment>{children}</React.Fragment>}
|
||||
{value === index && <Fragment>{children}</Fragment>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -218,6 +220,8 @@ const ViewBucket = ({
|
||||
const [replicationRules, setReplicationRules] = useState<
|
||||
BucketReplicationRule[]
|
||||
>([]);
|
||||
const [bucketPolicy, setBucketPolicy] = useState<Policy[]>([]);
|
||||
const [loadingPolicy, setLoadingPolicy] = useState<boolean>(true);
|
||||
const [loadingBucket, setLoadingBucket] = useState<boolean>(true);
|
||||
const [loadingEvents, setLoadingEvents] = useState<boolean>(true);
|
||||
const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
|
||||
@@ -230,6 +234,7 @@ const ViewBucket = ({
|
||||
);
|
||||
const [curTab, setCurTab] = useState<number>(0);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [policyScreenOpen, setPolicyScreenOpen] = useState<boolean>(false);
|
||||
const [
|
||||
enableEncryptionScreenOpen,
|
||||
setEnableEncryptionScreenOpen,
|
||||
@@ -244,9 +249,66 @@ const ViewBucket = ({
|
||||
const [retentionConfigOpen, setRetentionConfigOpen] = useState<boolean>(
|
||||
false
|
||||
);
|
||||
const [policyEdit, setPolicyEdit] = useState<any>(null);
|
||||
const [enableVersioningOpen, setEnableVersioningOpen] = useState<boolean>(
|
||||
false
|
||||
);
|
||||
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
|
||||
const [canPutReplication, setCanPutReplication] = useState<boolean>(false);
|
||||
const [canGetReplication, setCanGetReplication] = useState<boolean>(false);
|
||||
|
||||
// check the permissions for creating bucket
|
||||
useEffect(() => {
|
||||
if (loadingPerms) {
|
||||
api
|
||||
.invoke("POST", `/api/v1/has-permission`, {
|
||||
actions: [
|
||||
{
|
||||
id: "PutReplicationConfiguration",
|
||||
action: "s3:PutReplicationConfiguration",
|
||||
bucket_name: bucketName,
|
||||
},
|
||||
{
|
||||
id: "GetReplicationConfiguration",
|
||||
action: "s3:GetReplicationConfiguration",
|
||||
bucket_name: bucketName,
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((res: HasPermissionResponse) => {
|
||||
setLoadingPerms(false);
|
||||
if (!res.permissions) {
|
||||
return;
|
||||
}
|
||||
const actions = res.permissions ? res.permissions : [];
|
||||
|
||||
let canPutReplication = actions.find(
|
||||
(s) => s.id == "PutReplicationConfiguration"
|
||||
);
|
||||
|
||||
if (canPutReplication && canPutReplication.can) {
|
||||
setCanPutReplication(true);
|
||||
} else {
|
||||
setCanPutReplication(false);
|
||||
}
|
||||
let canGetReplication = actions.find(
|
||||
(s) => s.id == "GetReplicationConfiguration"
|
||||
);
|
||||
|
||||
if (canGetReplication && canGetReplication.can) {
|
||||
setCanGetReplication(true);
|
||||
} else {
|
||||
setCanGetReplication(false);
|
||||
}
|
||||
|
||||
setLoadingPerms(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoadingPerms(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loadingPerms, setErrorSnackMessage]);
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
@@ -312,6 +374,21 @@ const ViewBucket = ({
|
||||
}
|
||||
}, [loadingReplication, setErrorSnackMessage, bucketName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingPolicy) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/bucket-policy/${bucketName}`)
|
||||
.then((res: any) => {
|
||||
setBucketPolicy(res.policies);
|
||||
setLoadingPolicy(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingPolicy(false);
|
||||
});
|
||||
}
|
||||
}, [loadingPolicy, setErrorSnackMessage, bucketName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingSize) {
|
||||
api
|
||||
@@ -379,6 +456,18 @@ const ViewBucket = ({
|
||||
setLoadingEncryption(true);
|
||||
};
|
||||
|
||||
const closeAddModalAndRefresh = (refresh: boolean) => {
|
||||
setPolicyScreenOpen(false);
|
||||
|
||||
if (refresh) {
|
||||
fetchPolicies();
|
||||
}
|
||||
};
|
||||
|
||||
const fetchPolicies = () => {
|
||||
setLoadingPolicy(true);
|
||||
};
|
||||
|
||||
const closeAddEventAndRefresh = () => {
|
||||
setAddScreenOpen(false);
|
||||
loadAllBucketData();
|
||||
@@ -430,19 +519,15 @@ const ViewBucket = ({
|
||||
}
|
||||
|
||||
const eventsDisplay = (events: string[]) => {
|
||||
return <React.Fragment>{events.join(", ")}</React.Fragment>;
|
||||
return <Fragment>{events.join(", ")}</Fragment>;
|
||||
};
|
||||
|
||||
const ruleDestDisplay = (events: BucketReplicationDestination) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{events.bucket.replace("arn:aws:s3:::", "")}
|
||||
</React.Fragment>
|
||||
);
|
||||
return <Fragment>{events.bucket.replace("arn:aws:s3:::", "")}</Fragment>;
|
||||
};
|
||||
|
||||
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
|
||||
return <React.Fragment>{events.status}</React.Fragment>;
|
||||
return <Fragment>{events.status}</Fragment>;
|
||||
};
|
||||
|
||||
const setOpenReplicationOpen = (open = false) => {
|
||||
@@ -467,9 +552,15 @@ const ViewBucket = ({
|
||||
};
|
||||
|
||||
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
||||
const viewAction = (row: any) => {
|
||||
setPolicyScreenOpen(true);
|
||||
setPolicyEdit(row);
|
||||
};
|
||||
|
||||
const PolicyActions = [{ type: "view", onClick: viewAction }];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddEvent
|
||||
open={addScreenOpen}
|
||||
@@ -492,6 +583,13 @@ const ViewBucket = ({
|
||||
closeModalAndRefresh={closeSetAccessPolicy}
|
||||
/>
|
||||
)}
|
||||
{policyScreenOpen && (
|
||||
<AddPolicy
|
||||
open={policyScreenOpen}
|
||||
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||
policyEdit={policyEdit}
|
||||
/>
|
||||
)}
|
||||
{retentionConfigOpen && (
|
||||
<SetRetentionConfig
|
||||
bucketName={bucketName}
|
||||
@@ -573,10 +671,10 @@ const ViewBucket = ({
|
||||
<span>{replicationRules.length ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
{!hasObjectLocking && (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
<div>Object Locking:</div>
|
||||
<div>No</div>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
<div>Encryption:</div>
|
||||
<div>
|
||||
@@ -608,7 +706,7 @@ const ViewBucket = ({
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{isVersioned && !loadingVersioning ? "Yes" : "No"}
|
||||
|
||||
<IconButton
|
||||
@@ -622,7 +720,7 @@ const ViewBucket = ({
|
||||
>
|
||||
<PencilIcon active={true} />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
<div>Retention:</div>
|
||||
@@ -634,7 +732,7 @@ const ViewBucket = ({
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
|
||||
<IconButton
|
||||
color="primary"
|
||||
@@ -647,7 +745,7 @@ const ViewBucket = ({
|
||||
>
|
||||
<PencilIcon active={true} />
|
||||
</IconButton>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -671,7 +769,10 @@ const ViewBucket = ({
|
||||
aria-label="cluster-tabs"
|
||||
>
|
||||
<Tab label="Events" {...a11yProps(0)} />
|
||||
<Tab label="Replication" {...a11yProps(1)} />
|
||||
{canGetReplication && (
|
||||
<Tab label="Replication" {...a11yProps(1)} />
|
||||
)}
|
||||
<Tab label="Policies" {...a11yProps(2)} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={6} className={classes.actionsTray}>
|
||||
@@ -689,17 +790,21 @@ const ViewBucket = ({
|
||||
</Button>
|
||||
)}
|
||||
{curTab === 1 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setOpenReplicationOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Replication Rule
|
||||
</Button>
|
||||
<Fragment>
|
||||
{canPutReplication && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setOpenReplicationOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Replication Rule
|
||||
</Button>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -723,37 +828,49 @@ const ViewBucket = ({
|
||||
idField="id"
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={curTab}>
|
||||
{canGetReplication && (
|
||||
<TabPanel index={1} value={curTab}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "ID", elementKey: "id" },
|
||||
{
|
||||
label: "Priority",
|
||||
elementKey: "priority",
|
||||
},
|
||||
{
|
||||
label: "Destination",
|
||||
elementKey: "destination",
|
||||
renderFunction: ruleDestDisplay,
|
||||
},
|
||||
{
|
||||
label: "Delete Replication",
|
||||
elementKey: "delete_marker_replication",
|
||||
renderFunction: ruleDelDisplay,
|
||||
},
|
||||
{ label: "Status", elementKey: "status" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={replicationRules}
|
||||
entityName="Replication Rules"
|
||||
idField="id"
|
||||
/>
|
||||
</TabPanel>
|
||||
)}
|
||||
<TabPanel index={2} value={curTab}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "ID", elementKey: "id" },
|
||||
{
|
||||
label: "Priority",
|
||||
elementKey: "priority",
|
||||
},
|
||||
{
|
||||
label: "Destination",
|
||||
elementKey: "destination",
|
||||
renderFunction: ruleDestDisplay,
|
||||
},
|
||||
{
|
||||
label: "Delete Replication",
|
||||
elementKey: "delete_marker_replication",
|
||||
renderFunction: ruleDelDisplay,
|
||||
},
|
||||
{ label: "Status", elementKey: "status" },
|
||||
]}
|
||||
itemActions={PolicyActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingEvents}
|
||||
records={replicationRules}
|
||||
entityName="Replication Rules"
|
||||
idField="id"
|
||||
records={bucketPolicy}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -118,3 +118,12 @@ export interface IRemoteBucket {
|
||||
status: string;
|
||||
service: string;
|
||||
}
|
||||
|
||||
export interface PermissionAction {
|
||||
id: string;
|
||||
can: boolean;
|
||||
}
|
||||
|
||||
export interface HasPermissionResponse {
|
||||
permissions: PermissionAction[];
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, Fragment } from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import get from "lodash/get";
|
||||
@@ -26,7 +26,7 @@ import SearchIcon from "@material-ui/icons/Search";
|
||||
import { Button } from "@material-ui/core";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { Bucket, BucketList } from "../Buckets/types";
|
||||
import { Bucket, BucketList, HasPermissionResponse } from "../Buckets/types";
|
||||
import {
|
||||
actionsTray,
|
||||
objectBrowserCommon,
|
||||
@@ -118,6 +118,40 @@ const BrowseBuckets = ({
|
||||
const [records, setRecords] = useState<Bucket[]>([]);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
|
||||
const [canCreateBucket, setCanCreateBucket] = useState<boolean>(false);
|
||||
|
||||
// check the permissions for creating bucket
|
||||
useEffect(() => {
|
||||
if (loadingPerms) {
|
||||
api
|
||||
.invoke("POST", `/api/v1/has-permission`, {
|
||||
actions: [
|
||||
{
|
||||
id: "createBucket",
|
||||
action: "s3:CreateBucket",
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((res: HasPermissionResponse) => {
|
||||
const canCreate = res.permissions
|
||||
.filter((s) => s.id == "createBucket")
|
||||
.pop();
|
||||
if (canCreate && canCreate.can) {
|
||||
setCanCreateBucket(true);
|
||||
} else {
|
||||
setCanCreateBucket(false);
|
||||
}
|
||||
|
||||
setLoadingPerms(false);
|
||||
// setRecords(res.buckets || []);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoadingPerms(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loadingPerms, setErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
resetRoutesList(true);
|
||||
@@ -170,7 +204,7 @@ const BrowseBuckets = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddBucket
|
||||
open={addScreenOpen}
|
||||
@@ -182,18 +216,20 @@ const BrowseBuckets = ({
|
||||
<div>
|
||||
<BrowserBreadcrumbs />
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
</div>
|
||||
{canCreateBucket && (
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
@@ -251,7 +287,7 @@ const BrowseBuckets = ({
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1028,7 +1028,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
},
|
||||
params: admin_api.UpdateTenantParams{
|
||||
Body: &models.UpdateTenantRequest{
|
||||
ConsoleImage: "minio/console:v0.6.4",
|
||||
ConsoleImage: "minio/console:v0.6.6",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,7 +63,7 @@ const (
|
||||
// Image versions
|
||||
const (
|
||||
KESImageVersion = "minio/kes:v0.13.4"
|
||||
ConsoleImageDefaultVersion = "minio/console:v0.6.4"
|
||||
ConsoleImageDefaultVersion = "minio/console:v0.6.6"
|
||||
)
|
||||
|
||||
// K8s
|
||||
|
||||
@@ -1724,6 +1724,39 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/has-permission": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Checks whether the user can perform a series of actions",
|
||||
"operationId": "HasPermissionTo",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/hasPermissionRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/hasPermissionResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login": {
|
||||
"get": {
|
||||
"security": [],
|
||||
@@ -4094,6 +4127,28 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"hasPermissionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/policyArgs"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hasPermissionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/permissionAction"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iamEntity": {
|
||||
"type": "string",
|
||||
"pattern": "^[\\w+=,.@-]{1,64}$"
|
||||
@@ -4704,6 +4759,17 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"permissionAction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"can": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"podAffinityTerm": {
|
||||
"description": "Required. A pod affinity term, associated with the corresponding weight.",
|
||||
"type": "object",
|
||||
@@ -4777,6 +4843,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyArgs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyEntity": {
|
||||
"type": "string",
|
||||
"default": "user",
|
||||
@@ -7483,6 +7563,39 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/has-permission": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Checks whether the user can perform a series of actions",
|
||||
"operationId": "HasPermissionTo",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/hasPermissionRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/hasPermissionResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login": {
|
||||
"get": {
|
||||
"security": [],
|
||||
@@ -10462,6 +10575,28 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"hasPermissionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/policyArgs"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"hasPermissionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/permissionAction"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iamEntity": {
|
||||
"type": "string",
|
||||
"pattern": "^[\\w+=,.@-]{1,64}$"
|
||||
@@ -11028,6 +11163,17 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"permissionAction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"can": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"podAffinityTerm": {
|
||||
"description": "Required. A pod affinity term, associated with the corresponding weight.",
|
||||
"type": "object",
|
||||
@@ -11079,6 +11225,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyArgs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bucket_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyEntity": {
|
||||
"type": "string",
|
||||
"default": "user",
|
||||
|
||||
@@ -184,6 +184,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPIGroupInfoHandler: admin_api.GroupInfoHandlerFunc(func(params admin_api.GroupInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.GroupInfo has not yet been implemented")
|
||||
}),
|
||||
UserAPIHasPermissionToHandler: user_api.HasPermissionToHandlerFunc(func(params user_api.HasPermissionToParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.HasPermissionTo has not yet been implemented")
|
||||
}),
|
||||
AdminAPIListAllTenantsHandler: admin_api.ListAllTenantsHandlerFunc(func(params admin_api.ListAllTenantsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ListAllTenants has not yet been implemented")
|
||||
}),
|
||||
@@ -475,6 +478,8 @@ type ConsoleAPI struct {
|
||||
AdminAPIGetUserInfoHandler admin_api.GetUserInfoHandler
|
||||
// AdminAPIGroupInfoHandler sets the operation handler for the group info operation
|
||||
AdminAPIGroupInfoHandler admin_api.GroupInfoHandler
|
||||
// UserAPIHasPermissionToHandler sets the operation handler for the has permission to operation
|
||||
UserAPIHasPermissionToHandler user_api.HasPermissionToHandler
|
||||
// AdminAPIListAllTenantsHandler sets the operation handler for the list all tenants operation
|
||||
AdminAPIListAllTenantsHandler admin_api.ListAllTenantsHandler
|
||||
// UserAPIListBucketEventsHandler sets the operation handler for the list bucket events operation
|
||||
@@ -777,6 +782,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPIGroupInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.GroupInfoHandler")
|
||||
}
|
||||
if o.UserAPIHasPermissionToHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.HasPermissionToHandler")
|
||||
}
|
||||
if o.AdminAPIListAllTenantsHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ListAllTenantsHandler")
|
||||
}
|
||||
@@ -1198,6 +1206,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/groups/{name}"] = admin_api.NewGroupInfo(o.context, o.AdminAPIGroupInfoHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/has-permission"] = user_api.NewHasPermissionTo(o.context, o.UserAPIHasPermissionToHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
90
restapi/operations/user_api/has_permission_to.go
Normal file
90
restapi/operations/user_api/has_permission_to.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) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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/console/models"
|
||||
)
|
||||
|
||||
// HasPermissionToHandlerFunc turns a function with the right signature into a has permission to handler
|
||||
type HasPermissionToHandlerFunc func(HasPermissionToParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn HasPermissionToHandlerFunc) Handle(params HasPermissionToParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// HasPermissionToHandler interface for that can handle valid has permission to params
|
||||
type HasPermissionToHandler interface {
|
||||
Handle(HasPermissionToParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewHasPermissionTo creates a new http.Handler for the has permission to operation
|
||||
func NewHasPermissionTo(ctx *middleware.Context, handler HasPermissionToHandler) *HasPermissionTo {
|
||||
return &HasPermissionTo{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*HasPermissionTo swagger:route POST /has-permission UserAPI hasPermissionTo
|
||||
|
||||
Checks whether the user can perform a series of actions
|
||||
|
||||
*/
|
||||
type HasPermissionTo struct {
|
||||
Context *middleware.Context
|
||||
Handler HasPermissionToHandler
|
||||
}
|
||||
|
||||
func (o *HasPermissionTo) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewHasPermissionToParams()
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
94
restapi/operations/user_api/has_permission_to_parameters.go
Normal file
94
restapi/operations/user_api/has_permission_to_parameters.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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 (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewHasPermissionToParams creates a new HasPermissionToParams object
|
||||
// no default values defined in spec.
|
||||
func NewHasPermissionToParams() HasPermissionToParams {
|
||||
|
||||
return HasPermissionToParams{}
|
||||
}
|
||||
|
||||
// HasPermissionToParams contains all the bound params for the has permission to operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters HasPermissionTo
|
||||
type HasPermissionToParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.HasPermissionRequest
|
||||
}
|
||||
|
||||
// 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 NewHasPermissionToParams() beforehand.
|
||||
func (o *HasPermissionToParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.HasPermissionRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/user_api/has_permission_to_responses.go
Normal file
133
restapi/operations/user_api/has_permission_to_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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/console/models"
|
||||
)
|
||||
|
||||
// HasPermissionToCreatedCode is the HTTP code returned for type HasPermissionToCreated
|
||||
const HasPermissionToCreatedCode int = 201
|
||||
|
||||
/*HasPermissionToCreated A successful response.
|
||||
|
||||
swagger:response hasPermissionToCreated
|
||||
*/
|
||||
type HasPermissionToCreated struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.HasPermissionResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewHasPermissionToCreated creates HasPermissionToCreated with default headers values
|
||||
func NewHasPermissionToCreated() *HasPermissionToCreated {
|
||||
|
||||
return &HasPermissionToCreated{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the has permission to created response
|
||||
func (o *HasPermissionToCreated) WithPayload(payload *models.HasPermissionResponse) *HasPermissionToCreated {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the has permission to created response
|
||||
func (o *HasPermissionToCreated) SetPayload(payload *models.HasPermissionResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *HasPermissionToCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(201)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*HasPermissionToDefault Generic error response.
|
||||
|
||||
swagger:response hasPermissionToDefault
|
||||
*/
|
||||
type HasPermissionToDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewHasPermissionToDefault creates HasPermissionToDefault with default headers values
|
||||
func NewHasPermissionToDefault(code int) *HasPermissionToDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &HasPermissionToDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the has permission to default response
|
||||
func (o *HasPermissionToDefault) WithStatusCode(code int) *HasPermissionToDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the has permission to default response
|
||||
func (o *HasPermissionToDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the has permission to default response
|
||||
func (o *HasPermissionToDefault) WithPayload(payload *models.Error) *HasPermissionToDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the has permission to default response
|
||||
func (o *HasPermissionToDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *HasPermissionToDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/user_api/has_permission_to_urlbuilder.go
Normal file
104
restapi/operations/user_api/has_permission_to_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package 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"
|
||||
)
|
||||
|
||||
// HasPermissionToURL generates an URL for the has permission to operation
|
||||
type HasPermissionToURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *HasPermissionToURL) WithBasePath(bp string) *HasPermissionToURL {
|
||||
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 *HasPermissionToURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *HasPermissionToURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/has-permission"
|
||||
|
||||
_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 *HasPermissionToURL) 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 *HasPermissionToURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *HasPermissionToURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on HasPermissionToURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on HasPermissionToURL")
|
||||
}
|
||||
|
||||
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 *HasPermissionToURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
@@ -42,6 +44,15 @@ func registerAccountHandlers(api *operations.ConsoleAPI) {
|
||||
user_api.NewLoginCreated().WithPayload(changePasswordResponse).WriteResponse(w, p)
|
||||
})
|
||||
})
|
||||
// Checks if user can perform an action
|
||||
api.UserAPIHasPermissionToHandler = user_api.HasPermissionToHandlerFunc(func(params user_api.HasPermissionToParams, session *models.Principal) middleware.Responder {
|
||||
hasPermissionRespose, err := getUserHasPermissionsResponse(session, params)
|
||||
if err != nil {
|
||||
return user_api.NewHasPermissionToDefault(500).WithPayload(err)
|
||||
}
|
||||
// Custom response writer to update the session cookies
|
||||
return user_api.NewHasPermissionToCreated().WithPayload(hasPermissionRespose)
|
||||
})
|
||||
}
|
||||
|
||||
// changePassword validate current current user password and if it's correct set the new password
|
||||
@@ -92,3 +103,48 @@ func getChangePasswordResponse(session *models.Principal, params user_api.Accoun
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
|
||||
func getUserHasPermissionsResponse(session *models.Principal, params user_api.HasPermissionToParams) (*models.HasPermissionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
userPolicy, err := getAccountPolicy(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
var perms []*models.PermissionAction
|
||||
|
||||
for _, p := range params.Body.Actions {
|
||||
canPerform := userCanDo(iampolicy.Args{
|
||||
Action: iampolicy.Action(p.Action),
|
||||
BucketName: p.BucketName,
|
||||
}, userPolicy)
|
||||
perms = append(perms, &models.PermissionAction{
|
||||
Can: canPerform,
|
||||
ID: p.ID,
|
||||
})
|
||||
}
|
||||
|
||||
return &models.HasPermissionResponse{
|
||||
Permissions: perms,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userCanDo(arg iampolicy.Args, userPolicy *iampolicy.Policy) bool {
|
||||
// check in all the statements if any allows the passed action
|
||||
for _, stmt := range userPolicy.Statements {
|
||||
if stmt.IsAllowed(arg) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
iampolicy "github.com/minio/minio/pkg/iam/policy"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
@@ -106,3 +109,152 @@ func Test_changePassword(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_useCanDo(t *testing.T) {
|
||||
type args struct {
|
||||
arg iampolicy.Args
|
||||
userPolicy string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Create Bucket",
|
||||
args: args{
|
||||
arg: iampolicy.Args{
|
||||
Action: "s3:CreateBucket",
|
||||
},
|
||||
userPolicy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"admin:*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Create Bucket, No Admin",
|
||||
args: args{
|
||||
arg: iampolicy.Args{
|
||||
Action: "s3:CreateBucket",
|
||||
},
|
||||
userPolicy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Create Bucket, By Prefix",
|
||||
args: args{
|
||||
arg: iampolicy.Args{
|
||||
Action: "s3:CreateBucket",
|
||||
},
|
||||
userPolicy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Create Bucket, With Bucket Name",
|
||||
args: args{
|
||||
arg: iampolicy.Args{
|
||||
Action: "s3:CreateBucket",
|
||||
BucketName: "bucket2",
|
||||
},
|
||||
userPolicy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Can't Create Bucket",
|
||||
args: args{
|
||||
arg: iampolicy.Args{
|
||||
Action: "s3:CreateBucket",
|
||||
BucketName: "bucket2",
|
||||
},
|
||||
userPolicy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "VisualEditor1",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::bucket1",
|
||||
"arn:aws:s3:::bucket1/*",
|
||||
"arn:aws:s3:::lkasdkljasd090901",
|
||||
"arn:aws:s3:::lkasdkljasd090901/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var pol iampolicy.Policy
|
||||
if err := json.Unmarshal([]byte(tt.args.userPolicy), &pol); err != nil {
|
||||
t.Errorf("Policy can't be parsed: %s", err)
|
||||
}
|
||||
if got := userCanDo(tt.args.arg, &pol); got != tt.want {
|
||||
t.Errorf("userCanDo() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +324,7 @@ func getAccountInfo(ctx context.Context, client MinioAdmin) ([]*models.Bucket, e
|
||||
if err != nil {
|
||||
return []*models.Bucket{}, err
|
||||
}
|
||||
|
||||
var bucketInfos []*models.Bucket
|
||||
for _, bucket := range info.Buckets {
|
||||
bucketElem := &models.Bucket{Name: swag.String(bucket.Name), CreationDate: bucket.Created.Format(time.RFC3339), Size: int64(bucket.Size)}
|
||||
|
||||
81
swagger.yml
81
swagger.yml
@@ -946,6 +946,28 @@ paths:
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/has-permission:
|
||||
post:
|
||||
summary: Checks whether the user can perform a series of actions
|
||||
operationId: HasPermissionTo
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/hasPermissionRequest"
|
||||
responses:
|
||||
201:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/hasPermissionResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/users:
|
||||
get:
|
||||
summary: List Users
|
||||
@@ -1557,14 +1579,14 @@ paths:
|
||||
summary: Activate a particular tenant using the existing subscription license
|
||||
operationId: SubscriptionActivate
|
||||
parameters:
|
||||
- name: namespace
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: tenant
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: namespace
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: tenant
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
204:
|
||||
description: A successful response.
|
||||
@@ -2145,7 +2167,7 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
|
||||
/direct-csi/drives:
|
||||
get:
|
||||
summary: Get direct-csi drives list
|
||||
@@ -2170,7 +2192,7 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
|
||||
/direct-csi/volumes:
|
||||
get:
|
||||
summary: Get direct-csi volumes list
|
||||
@@ -2778,7 +2800,7 @@ definitions:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [ok]
|
||||
enum: [ ok ]
|
||||
operator:
|
||||
type: boolean
|
||||
widgetResult:
|
||||
@@ -4050,7 +4072,7 @@ definitions:
|
||||
restart:
|
||||
description: Returns wheter server needs to restart to apply changes or not
|
||||
type: boolean
|
||||
|
||||
|
||||
|
||||
subscriptionValidateRequest:
|
||||
type: object
|
||||
@@ -4105,7 +4127,7 @@ definitions:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
|
||||
|
||||
getDirectCSIVolumeListResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -4113,7 +4135,7 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/directCSIVolumeInfo"
|
||||
|
||||
|
||||
directCSIVolumeInfo:
|
||||
type: object
|
||||
properties:
|
||||
@@ -4126,3 +4148,34 @@ definitions:
|
||||
type: string
|
||||
drive:
|
||||
type: string
|
||||
|
||||
policyArgs:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
action:
|
||||
type: string
|
||||
bucket_name:
|
||||
type: string
|
||||
hasPermissionRequest:
|
||||
type: object
|
||||
properties:
|
||||
actions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/policyArgs"
|
||||
permissionAction:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
can:
|
||||
type: boolean
|
||||
hasPermissionResponse:
|
||||
type: object
|
||||
properties:
|
||||
permissions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/permissionAction"
|
||||
|
||||
Reference in New Issue
Block a user