Compare commits

..

22 Commits

Author SHA1 Message Date
Minio Trusted
d573007747 update console to v0.4.6 2020-11-24 14:00:52 -08:00
Lenin Alevski
f054b1c251 Ensure GetConsoleSTSClient() is used correctly (#425) 2020-11-20 13:50:35 -08:00
Lenin Alevski
7a2358272a Get LDAP identity for console access/secret keys (#398)
- If MinIO is configured with LDAP then users and groups are external, and
  the credentials provided in the CONSOLE_ACCESS_KEY and
  CONSOLE_SECRET_KEY env vars will belong to an existing user in the active
  directory, therefore we need to authenticate first with
  `credentials.NewLDAPIdentity`
- Fixed race condition bug in which TLS RootCAs certs were not loading
  correctly (certPool was always null)
- Fixed TLS bug in which if Console was deployed without TLS enabled
  RootCAs certs were not loading
- Initialize LDAP Admin credentials once
- Initialize stsClient once
2020-11-20 11:52:34 -08:00
Alex
8a6a75b7a2 Connected object tags API (#421)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-11-19 21:30:33 -08:00
Cesar N
8cf678fb27 Replace mc with latest mc on go mod (#424) 2020-11-19 15:23:31 -08:00
Cesar N
a20c6dc907 Add download version object on download api (#423) 2020-11-19 15:04:13 -08:00
Daniel Valdivia
37ff8bb60d Fix Tenant Details header (#420)
Also renames zones to servers
2020-11-19 11:45:30 -08:00
Lenin Alevski
b6ac055857 Show error message if not possible to create CAs folder (#407)
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-18 22:46:54 -08:00
Cesar N
12e53a1468 Remove minio root credentials from CreateTenant response (#402)
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-18 16:16:06 -08:00
Alex
b21123e1cd Connected delete & download buttons for object details (#399)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-18 16:06:34 -08:00
Lenin Alevski
f1db949abc Fixes Console issue #400 (#401)
Previously cookie path was set to Path="/api", this was a performance
improvement to tell the browser to send the cookie only to request with
that prefix, however also consume endpoints on Path="/ws", since rfc6265
doesnt support multiple paths or regular expressions in the path field
of a cookie we are back to use Path="/" which means send the cookie to
all request under the current domain.

Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-18 14:42:02 -08:00
Alex
761c6529a2 Fixed issue with back arrows & navigation in object browser (#397)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-11-17 22:05:46 -08:00
Alex
1f832fa294 Connected Object details page to navigation (#394)
Connected Object details page to navigation schema. Also connected file details view with backend

Co-authored-by: Kaan Kabalak <kaan@minio.io>
2020-11-17 13:03:50 -08:00
Lenin Alevski
be569aee4f Support for Cookie authentication (#390)
- Added support for cookie authentication (authorization header will have priority)
- Removed local storage token management from UI
- cookie hardening (sameSite, httpOnly, secure)
- login endpoint sets cookie via header, logout endpoint expires cookie
- Refactor Routes and ProtectedRoutes components, improvement on the way
  application check if user session is valid

Future improvements

- look for all places in backend that returns 401 unauthorized, and destroy session there (not a priority since cookie its invalid anyway)
- Downloading objects in object browser can be simplified since is just a GET request and users will be authenticated via Cookies, no need to craft additional requests
2020-11-13 16:26:03 -08:00
Minio Trusted
419e94ccec update to v0.4.5 2020-11-13 11:10:13 -08:00
Alex
12bc5265b8 Fixed issue with object browser icons & long names (#389)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-12 14:33:56 -08:00
Kaan Kabalak
32898f0c57 Edit Searchbar styling based on mockups (#385)
Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-12 12:05:40 -08:00
Kaan Kabalak
125c9abf56 Adjust Modal form clear button font based on mockups (#384)
The font of the clear button for Modal forms were 'sans-serif' instead
of being 'Lato' as specified in the mockups.

Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
2020-11-12 10:28:49 -08:00
Alex
bc27db4a69 Migrated tablewrapper to use react-virtualized (#387)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-11-11 20:14:48 -08:00
Kaan Kabalak
dd8e2b13d3 Add disabled functionality and fix styling for RadioGroupSelector (#383)
* Adjust RadioGroupSelector label styling based on mockups

* Add disabled support for Radio Group Selector options
2020-11-10 18:59:21 -08:00
Alex
005e3b941c Fixed issue with checkbox selection in table wrapper (#380)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-11-10 00:08:45 -08:00
Alex
efa614c773 Fixed default value for nulls in browse buckets (#379)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
2020-11-06 16:47:07 -08:00
86 changed files with 2707 additions and 981 deletions

View File

@@ -11,40 +11,7 @@ $ docker run --rm -p 389:389 -p 636:636 --name my-openldap-container --detach os
Run the `billy.ldif` file using `ldapadd` command to create a new user and assign it to a group.
```
$ cat > billy.ldif << EOF
# LDIF fragment to create group branch under root
dn: uid=billy,dc=example,dc=org
uid: billy
cn: billy
sn: 3
objectClass: top
objectClass: posixAccount
objectClass: inetOrgPerson
loginShell: /bin/bash
homeDirectory: /home/billy
uidNumber: 14583102
gidNumber: 14564100
userPassword: {SSHA}j3lBh1Seqe4rqF1+NuWmjhvtAni1JC5A
mail: billy@example.org
gecos: Billy User
# Create base group
dn: ou=groups,dc=example,dc=org
objectclass:organizationalunit
ou: groups
description: generic groups branch
# create consoleAdmin group (this already exists on minio and have a policy of s3::*)
dn: cn=consoleAdmin,ou=groups,dc=example,dc=org
objectClass: top
objectClass: posixGroup
gidNumber: 678
# Assing group to new user
dn: cn=consoleAdmin,ou=groups,dc=example,dc=org
changetype: modify
add: memberuid
memberuid: billy
EOF
$ docker cp billy.ldif my-openldap-container:/container/service/slapd/assets/test/billy.ldif
$ docker cp console/docs/ldap/billy.ldif my-openldap-container:/container/service/slapd/assets/test/billy.ldif
$ docker exec my-openldap-container ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin -f /container/service/slapd/assets/test/billy.ldif -H ldap://localhost -ZZ
```

View File

@@ -28,8 +28,6 @@ import (
"github.com/minio/console/pkg/certs"
"github.com/minio/console/restapi"
"github.com/minio/console/restapi/operations"
"github.com/minio/minio/cmd/logger"
certsx "github.com/minio/minio/pkg/certs"
)
// starts the server
@@ -107,19 +105,18 @@ func startServer(ctx *cli.Context) error {
restapi.Hostname = ctx.String("host")
restapi.Port = fmt.Sprintf("%v", ctx.Int("port"))
// Set all certs and CAs directories.
// Set all certs and CAs directories path
certs.GlobalCertsDir, _ = certs.NewConfigDirFromCtx(ctx, "certs-dir", certs.DefaultCertsDir.Get)
certs.GlobalCertsCADir = &certs.ConfigDir{Path: filepath.Join(certs.GlobalCertsDir.Get(), certs.CertsCADir)}
logger.FatalIf(certs.MkdirAllIgnorePerm(certs.GlobalCertsCADir.Get()), "Unable to create certs CA directory at %s", certs.GlobalCertsCADir.Get())
// load all CAs from ~/.console/certs/CAs
restapi.GlobalRootCAs, err = certsx.GetRootCAs(certs.GlobalCertsCADir.Get())
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
// load all certs from ~/.console/certs
restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager, err = certs.GetTLSConfig()
logger.FatalIf(err, "Unable to load the TLS configuration")
// check if certs and CAs directories exists or can be created
if err := certs.MkdirAllIgnorePerm(certs.GlobalCertsCADir.Get()); err != nil {
log.Println(fmt.Sprintf("Unable to create certs CA directory at %s", certs.GlobalCertsCADir.Get()))
}
// load the certificates and the CAs
restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager = certs.GetAllCertificatesAndCAs()
if len(restapi.GlobalPublicCerts) > 0 && restapi.GlobalRootCAs != nil {
if len(restapi.GlobalPublicCerts) > 0 {
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
// plain HTTP connections to HTTPS server
server.EnabledListeners = []string{"http", "https"}

35
docs/ldap/billy.ldif Normal file
View File

@@ -0,0 +1,35 @@
# LDIF fragment to create group branch under root
dn: uid=billy,dc=example,dc=org
uid: billy
cn: billy
sn: 3
objectClass: top
objectClass: posixAccount
objectClass: inetOrgPerson
loginShell: /bin/bash
homeDirectory: /home/billy
uidNumber: 14583102
gidNumber: 14564100
userPassword: {SSHA}j3lBh1Seqe4rqF1+NuWmjhvtAni1JC5A
mail: billy@example.org
gecos: Billy User
# Create base group
dn: ou=groups,dc=example,dc=org
objectclass:organizationalunit
ou: groups
description: generic groups branch
# create consoleAdmin group (this already exists on minio and have a policy of s3::*)
dn: cn=consoleAdmin,ou=groups,dc=example,dc=org
objectClass: top
objectClass: posixGroup
gidNumber: 678
# Assing group to new user
dn: cn=consoleAdmin,ou=groups,dc=example,dc=org
changetype: modify
add: memberuid
memberuid: billy

10
go.mod
View File

@@ -16,17 +16,17 @@ require (
github.com/jessevdk/go-flags v1.4.0
github.com/minio/cli v1.22.0
github.com/minio/kes v0.11.0
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
github.com/minio/mc v0.0.0-20201119214335-d4f9ea859d6c
github.com/minio/minio v0.0.0-20201102034248-d8e07f2c41c8
github.com/minio/minio-go/v7 v7.0.6-0.20201119032702-6914cb678dde
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b
github.com/mitchellh/go-homedir v1.1.0
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/secure-io/sio-go v0.3.1
github.com/stretchr/testify v1.6.1
github.com/unrolled/secure v1.0.7
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/net v0.0.0-20200904194848-62affa334b73
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.18.6

188
go.sum
View File

@@ -2,7 +2,6 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
@@ -14,7 +13,6 @@ cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0 h1:EpMNVUorLiZIELdMZbCYX/ByTFCdoYopYAGxaGVz9ms=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
@@ -31,7 +29,6 @@ cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjp
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0 h1:86K1Gel7BQ9/WmNWn7dTKMvTLFzwtBe5FNqYbi9X35g=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
code.gitea.io/sdk/gitea v0.12.0/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
@@ -40,12 +37,9 @@ contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.apache.org/thrift.git v0.13.0 h1:/3bz5WZ+sqYArk7MBBBbDufMxKKOA56/6JO6psDpUDY=
git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -53,34 +47,26 @@ github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v42.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o=
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.9.1 h1:xjPqigMQe2+0DAJ5A6MLUPp5D2r2Io8qHCuCMMI/yJU=
github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
@@ -90,10 +76,8 @@ github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocm
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -125,7 +109,6 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrU
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
github.com/alecthomas/participle v0.2.1 h1:4AVLj1viSGa4LG5HDXKXrm5xRx19SB/rS/skPQB1Grw=
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/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -144,7 +127,6 @@ github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQh
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
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/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
@@ -160,11 +142,9 @@ github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go v1.29.11/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
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=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -182,12 +162,11 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.28 h1:kWGpdAcSp3MxMU9CCHOwz/8V0kCHN4+9yQm2MzWuI98=
github.com/cheggaaa/pb v1.0.28/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -199,10 +178,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/colinmarc/hdfs/v2 v2.1.1 h1:x0hw/m+o3UE20Scso/KCkvYNc9Di39TBlCfGMkJ1/a0=
github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c=
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A=
github.com/coredns/coredns v1.4.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -211,7 +188,6 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -231,29 +207,26 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/djherbis/atime v1.0.0 h1:ySLvBAM0EvOGaX7TI4dAM5lWj+RdJUCKtGSEHN8SGBg=
github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 h1:2HQmlpI3yI9deH18Q6xiSOIjXD4sLI55Y/gfpa8/558=
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE=
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d h1:QK8IYltsNy+5QZcDFbVkyInrs98/wHy1tfUTGG91sps=
github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d/go.mod h1:apXo4PA/BgBPrt66j0N45O2stlBTRowdip2igwcUWVc=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -278,7 +251,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -289,12 +261,10 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5GPSg=
github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -306,7 +276,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.57.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -435,14 +404,11 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -459,7 +425,6 @@ github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
@@ -468,7 +433,6 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
@@ -496,19 +460,15 @@ github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunE
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.1.2 h1:YjFNKqxzWUVZND8d4ItF9wuYlE75WQfECE7yKX/Nu3o=
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -534,33 +494,25 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.2 h1:DcFegQ7+ECdmkJMfVwWlC+89I4esJ7p8nkGt9ainGDk=
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/goreleaser/goreleaser v0.136.0/go.mod h1:wiKrPUeSNh6Wu8nUHxZydSOVQ/OZvOaO7DTtFqie904=
github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w=
github.com/goreleaser/nfpm v1.3.0/go.mod h1:w0p7Kc9TAUgWMyrub63ex3M2Mgw88M4GZXoTq5UCb40=
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.5-0.20200711200521-98cb6bf42e08 h1:kPna6oIGlRXWmg/jkKfxbpvsl+0DHYnw1qQwN+6+gyA=
github.com/gorilla/mux v1.7.5-0.20200711200521-98cb6bf42e08/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk=
github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -584,11 +536,9 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM=
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
@@ -601,16 +551,12 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -621,11 +567,9 @@ github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -634,19 +578,15 @@ github.com/hashicorp/raft v1.1.2 h1:oxEL5DDeurYxLd3UbcY/hccgSPhLLpiBZ1YxtWEq59c=
github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8=
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
@@ -673,7 +613,6 @@ github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBv
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -696,40 +635,30 @@ github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eT
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us=
github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0=
github.com/klauspost/readahead v1.3.1/go.mod h1:AH9juHzNH7xqdqFHrMRSHeH2Ps+vFf+kblDqzPFiLJg=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
@@ -752,9 +681,7 @@ github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -765,13 +692,12 @@ github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -783,7 +709,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.8 h1:1QYRAKU3lN5cRfLCkPU08hwvLJFhvjP6MqNMmQz6ZVI=
github.com/miekg/dns v1.1.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/cli v1.22.0 h1:VTQm7lmXm3quxO917X3p+el1l0Ca5X3S4PM2ruUYO68=
github.com/minio/cli v1.22.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
@@ -791,32 +716,27 @@ github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
github.com/minio/kes v0.11.0 h1:8ma6OCVSxKT50b1uYXLJro3m7PmZtCLxBaTddQexI5k=
github.com/minio/kes v0.11.0/go.mod h1:mTF1Bv8YVEtQqF/B7Felp4tLee44Pp+dgI0rhCvgNg8=
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821 h1:+09Ta2rY29df6U4bfSC85t7jxlNuYpUfl7lIVgTiDEA=
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821/go.mod h1:+OeGPfw7qpvO1x2Td65ejwAuFT0Cxzx0GFTJSDlPMZQ=
github.com/minio/mc v0.0.0-20201119214335-d4f9ea859d6c h1:/wGiTfC8pFOC2JzwE+XrPeH31LqFR/Dam4Lx1zX3Wag=
github.com/minio/mc v0.0.0-20201119214335-d4f9ea859d6c/go.mod h1:B738ZPTRQiyp3uJ3p2W2mqk8Yp1puYASKY7GIhYyHP8=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio v0.0.0-20200723003940-b9be841fd222 h1:+XFGpEsqmA033nDX8LtjyPZy01Shivf6E2OL67WoGiE=
github.com/minio/minio v0.0.0-20200723003940-b9be841fd222/go.mod h1:Eu2KC2p+vW03rnYY/6R/D+QduPB7/j4kBaVA/EDLjWM=
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544 h1:G6M9uXdFShowoLG3rMkdCtHsx37ZYB1vc+7bu22r85I=
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544/go.mod h1:5uolst3SWpgvDNF+KJwfViU+j9WH7dB5i2YCi0nMNVo=
github.com/minio/minio v0.0.0-20201102034248-d8e07f2c41c8 h1:yCDFjUBUoE1uNr8dEqjHuwySdAtOyvVONchEVrPPOGQ=
github.com/minio/minio v0.0.0-20201102034248-d8e07f2c41c8/go.mod h1:640kMkCwiyOX8dheptHYModdUw4HrnUFHKw3McqUXD8=
github.com/minio/minio-go/v7 v7.0.1/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns=
github.com/minio/minio-go/v7 v7.0.2 h1:P/7wFd4KrRBHVo7AKdcqO+9ReoS+XpMjfRFoE5quH0E=
github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns=
github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNHOQxQmwIFWs7YDLMTBh59r2XQ6RqEUA1I=
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
github.com/minio/minio-go/v7 v7.0.6-0.20200929220449-755b5633803a/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
github.com/minio/minio-go/v7 v7.0.6-0.20201119032702-6914cb678dde h1:Ag2kFFpCxct9ayux92WQUlT938y7+IbibERYOraT8dM=
github.com/minio/minio-go/v7 v7.0.6-0.20201119032702-6914cb678dde/go.mod h1:HcIuq+11d/3MfavIPZiswSzfQ1VJ2Lwxp/XLtW46IWQ=
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b h1:ggfD6V3nodTzhHJHCYIT1F897gscrz+hHsan0a2Wtqw=
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b/go.mod h1:At+++4/6W5BEXK11tN5DKIvkJKhYBZybbb5zmxb0LQI=
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37 h1:pDeao6M5AEd8hwTtGmE0pVKomlL56JFRa5SiXDZAuJE=
github.com/minio/simdjson-go v0.1.5-0.20200303142138-b17fe061ea37/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI=
github.com/minio/simdjson-go v0.1.5 h1:6T5mHh7r3kUvgwhmFWQAjoPV5Yt5oD/VPjAI9ViH1kM=
github.com/minio/simdjson-go v0.1.5/go.mod h1:oKURrZZEBtqObgJrSjN1Ln2n9MJj2icuBTkeJzZnvSI=
github.com/minio/sio v0.2.0 h1:NCRCFLx0r5pRbXf65LVNjxbCGZgNQvNFQkgX3XF4BoA=
github.com/minio/sio v0.2.0/go.mod h1:nKM5GIWSrqbOZp0uhyj6M1iA0X6xQzSGtYSaTKSCut0=
github.com/minio/sio v0.2.1/go.mod h1:8b0yPp2avGThviy/+OCJBI6OMpvxoUuiLvE6F1lebhw=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -829,13 +749,11 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 h1:UCU8+cLbbvyxi0sQ9fSeoEhZgvrrD9HKMtX6Gmc1vk8=
github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -864,7 +782,6 @@ github.com/nats-io/nats-streaming-server v0.18.0 h1:+RDozeN9scwCm0Wc2fYlvGcP144h
github.com/nats-io/nats-streaming-server v0.18.0/go.mod h1:Y9Aiif2oANuoKazQrs4wXtF3jqt6p97ODQg68lR5TnY=
github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY=
github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
@@ -887,13 +804,11 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
@@ -913,14 +828,13 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.4.0+incompatible h1:06usnXXDNcPvCHDkmPpkidf4jTc52UKld7UPfqKatY4=
github.com/pierrec/lz4 v2.4.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -943,7 +857,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -972,7 +885,6 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
@@ -983,13 +895,11 @@ github.com/ryancurrah/gomodguard v1.0.4/go.mod h1:9T/Cfuxs5StfsocWr4WzDL36HqnX0f
github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/secure-io/sio-go v0.3.0 h1:QKGb6rGJeiExac9wSWxnWPYo8O8OFN7lxXQvHshX6vo=
github.com/secure-io/sio-go v0.3.0/go.mod h1:D3KmXgKETffyYxBdFRN+Hpd2WzhzqS0EQwT3XWsAcBU=
github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
@@ -1007,18 +917,15 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -1029,7 +936,6 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -1053,7 +959,6 @@ 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 v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -1067,23 +972,19 @@ github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0
github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ=
github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.0.4 h1:UcdIRXff12Lpnu3OLtZvnc03g4vH2suXDXhBwBqmzYg=
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -1108,11 +1009,8 @@ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6Jc
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA=
github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
@@ -1139,16 +1037,13 @@ go.etcd.io/etcd/v3 v3.3.0-rc.0.0.20200707003333-58bb8ae09f8e h1:HZQLoe71Q24wVyDr
go.etcd.io/etcd/v3 v3.3.0-rc.0.0.20200707003333-58bb8ae09f8e/go.mod h1:UENlOa05tkNvLx9VnNziSerG4Ro74upGK6Apd4v6M/Y=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.3.0 h1:ew6uUIeJOo+qdUUv7LxFCUhtWmVv7ZV/Xuy4FAUsw2E=
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.mongodb.org/mongo-driver v1.3.4 h1:zs/dKNwX0gYUtzwrN9lLiR15hCO0nDwQj5xXx+vjCdE=
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1186,15 +1081,14 @@ golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1215,7 +1109,6 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
@@ -1227,7 +1120,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -1270,19 +1162,17 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
@@ -1292,12 +1182,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1348,28 +1235,26 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200915084602-288bc346aa39 h1:356XA7ITklAU2//sYkjFeco+dH1bCRD8XCJ9FIEsvo4=
golang.org/x/sys v0.0.0-20200915084602-288bc346aa39/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb h1:HS9IzC4UFbpMBLQUDSQcU+ViVT1vdFCQVjdPVpTlZrs=
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
@@ -1441,21 +1326,16 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj8tIM0KixlUUnBtYXsmd8=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200814172026-c4923e618c08 h1:sfBQLM20fzeXhOixVQirwEbuW4PGStP773EXQpsBB6E=
golang.org/x/tools v0.0.0-20200814172026-c4923e618c08/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f h1:7+Nz9MyPqt2qMCTvNiRy1G0zYfkB7UCa+ayT6uVvbyI=
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1463,7 +1343,6 @@ gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
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=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
@@ -1479,12 +1358,10 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.25.0 h1:LodzhlzZEUfhXzNUMIfVlf9Gr6Ua5MMtoFWh7+f47qA=
google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -1500,7 +1377,6 @@ google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
@@ -1526,12 +1402,10 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1543,26 +1417,19 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -1581,16 +1448,13 @@ gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
gopkg.in/jcmturner/gokrb5.v7 v7.2.3 h1:hHMV/yKPwMnJhPuPx7pH2Uw/3Qyf+thJYlisUc44010=
gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8Z4=
gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
gopkg.in/ldap.v3 v3.0.3 h1:YKRHW/2sIl05JsCtx/5ZuUueFuJyoj/6+DGXe3wp6ro=
gopkg.in/ldap.v3 v3.0.3/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/olivere/elastic.v5 v5.0.80 h1:AKjfcq3ZIAAqO4m8h/vJ3GP6nY8n9ft5mgf54fEqC60=
gopkg.in/olivere/elastic.v5 v5.0.80/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0=
gopkg.in/olivere/elastic.v5 v5.0.86 h1:xFy6qRCGAmo5Wjx96srho9BitLhZl2fcnpuidPwduXM=
gopkg.in/olivere/elastic.v5 v5.0.86/go.mod h1:M3WNlsF+WhYn7api4D87NIflwTV/c0iVs8cqfWhK+68=
@@ -1608,7 +1472,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1621,7 +1484,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.4.4
image: minio/console:v0.4.6
imagePullPolicy: "IfNotPresent"
args:
- server

View File

@@ -15,7 +15,7 @@ spec:
serviceAccountName: console-sa
containers:
- name: console
image: minio/console:v0.4.4
image: minio/console:v0.4.6
imagePullPolicy: "IfNotPresent"
env:
- name: CONSOLE_OPERATOR_MODE

View File

@@ -33,14 +33,8 @@ import (
// swagger:model createTenantResponse
type CreateTenantResponse struct {
// access key
AccessKey string `json:"access_key,omitempty"`
// console
Console *CreateTenantResponseConsole `json:"console,omitempty"`
// secret key
SecretKey string `json:"secret_key,omitempty"`
}
// Validate validates this create tenant response

View File

@@ -177,7 +177,7 @@ func TestGetActionsStringFromPolicy(t *testing.T) {
args: args{
policy: &iampolicy.AdminDiagnostics,
},
want: 6,
want: 7,
},
}
for _, tt := range tests {

View File

@@ -18,7 +18,7 @@ package auth
import (
"errors"
"log"
"net/http"
"github.com/minio/minio-go/v7/pkg/credentials"
)
@@ -27,13 +27,14 @@ var (
errInvalidCredentials = errors.New("invalid Login")
)
// GetConsoleCredentialsFromLDAP authenticates the user against MinIO when the LDAP integration is enabled
// GetCredentialsFromLDAP authenticates the user against MinIO when the LDAP integration is enabled
// if the authentication succeed *credentials.Login object is returned and we continue with the normal STSAssumeRole flow
func GetConsoleCredentialsFromLDAP(endpoint, ldapUser, ldapPassword string) (*credentials.Credentials, error) {
creds, err := credentials.NewLDAPIdentity(endpoint, ldapUser, ldapPassword)
if err != nil {
log.Println("LDAP authentication error: ", err)
return nil, errInvalidCredentials
}
func GetCredentialsFromLDAP(client *http.Client, endpoint, ldapUser, ldapPassword string) (*credentials.Credentials, error) {
creds := credentials.New(&credentials.LDAPIdentity{
Client: client,
STSEndpoint: endpoint,
LDAPUsername: ldapUser,
LDAPPassword: ldapPassword,
})
return creds, nil
}

View File

@@ -29,6 +29,7 @@ import (
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/certs"
certsx "github.com/minio/minio/pkg/certs"
"github.com/mitchellh/go-homedir"
)
@@ -220,3 +221,13 @@ func GetTLSConfig() (x509Certs []*x509.Certificate, manager *certs.Manager, err
}
return x509Certs, manager, nil
}
func GetAllCertificatesAndCAs() (*x509.CertPool, []*x509.Certificate, *certs.Manager) {
// load all CAs from ~/.console/certs/CAs
GlobalRootCAs, err := certsx.GetRootCAs(GlobalCertsCADir.Get())
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
// load all certs from ~/.console/certs
globalPublicCerts, globalTLSCertsManager, err := GetTLSConfig()
logger.FatalIf(err, "Unable to load the TLS configuration")
return GlobalRootCAs, globalPublicCerts, globalTLSCertsManager
}

File diff suppressed because one or more lines are too long

View File

@@ -1827,6 +1827,14 @@
"csstype": "^2.2.0"
}
},
"@types/react-copy-to-clipboard": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.3.0.tgz",
"integrity": "sha512-iideNPRyroENqsOFh1i2Dv3zkviYS9r/9qD9Uh3Z9NNoAAqqa2x53i7iGndGNnJFIo20wIu7Hgh77tx1io8bgw==",
"requires": {
"@types/react": "*"
}
},
"@types/react-dom": {
"version": "16.9.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz",
@@ -3943,6 +3951,14 @@
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
},
"copy-to-clipboard": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
"integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
"requires": {
"toggle-selection": "^1.0.6"
}
},
"core-js": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
@@ -13932,6 +13948,15 @@
"resolved": "https://registry.npmjs.org/react-codemirror2/-/react-codemirror2-7.1.0.tgz",
"integrity": "sha512-Rel0QbPnCTjHxgZYt6TkGw4icSZXNyONHb72a+1wWA+PlYJIvzFAv4pZlDPG0rpKpKmy4kSUlkoWgneH7w3A0g=="
},
"react-copy-to-clipboard": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz",
"integrity": "sha512-/2t5mLMMPuN5GmdXo6TebFa8IoFxZ+KTDDqYhcDm0PhkgEzSxVvIX26G20s1EB02A4h2UZgwtfymZ3lGJm0OLg==",
"requires": {
"copy-to-clipboard": "^3",
"prop-types": "^15.5.8"
}
},
"react-dev-utils": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz",
@@ -16325,6 +16350,11 @@
"repeat-string": "^1.6.1"
}
},
"toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",

View File

@@ -13,10 +13,12 @@
"@types/lodash": "^4.14.149",
"@types/node": "12.12.8",
"@types/react": "16.9.11",
"@types/react-copy-to-clipboard": "^4.3.0",
"@types/react-dom": "16.9.4",
"@types/react-redux": "^7.1.5",
"@types/react-router": "^5.1.3",
"@types/react-router-dom": "^5.1.2",
"@types/react-virtualized": "^9.21.10",
"@types/recharts": "^1.8.9",
"@types/superagent": "^4.1.4",
"@types/webpack-env": "^1.14.1",
@@ -34,12 +36,15 @@
"react-async-hook": "^3.6.1",
"react-chartjs-2": "^2.9.0",
"react-codemirror2": "^7.1.0",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "^16.12.0",
"react-hot-loader": "^4.13.0",
"react-moment": "^0.9.7",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.4",
"react-virtualized": "^9.22.2",
"react-window-infinite-loader": "^1.0.5",
"recharts": "^1.8.5",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",

View File

@@ -0,0 +1,68 @@
// 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, useState } from "react";
import { Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { AppState } from "./store";
import { userLoggedIn } from "./actions";
import api from "./common/api";
import { clearSession } from "./common/utils";
import { saveSessionResponse } from "./screens/Console/actions";
const mapState = (state: AppState) => ({
loggedIn: state.system.loggedIn,
});
const connector = connect(mapState, {
userLoggedIn,
saveSessionResponse,
});
interface ProtectedRouteProps {
loggedIn: boolean;
Component: any;
userLoggedIn: typeof userLoggedIn;
saveSessionResponse: typeof saveSessionResponse;
}
const ProtectedRoute = ({
Component,
loggedIn,
userLoggedIn,
saveSessionResponse,
}: ProtectedRouteProps) => {
const [sessionLoading, setSessionLoading] = useState<boolean>(true);
useEffect(() => {
api
.invoke("GET", `/api/v1/session`)
.then((res) => {
saveSessionResponse(res);
userLoggedIn(true);
setSessionLoading(false);
})
.catch(() => setSessionLoading(false));
}, [saveSessionResponse]);
// if we still trying to retrieve user session render nothing
if (sessionLoading) {
return null;
}
// redirect user to the right page based on session status
return loggedIn ? <Component /> : <Redirect to={{ pathname: "/login" }} />;
};
export default connector(ProtectedRoute);

View File

@@ -15,65 +15,24 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Redirect, Route, Router, Switch } from "react-router-dom";
import { Route, Router, Switch } from "react-router-dom";
import history from "./history";
import Login from "./screens/LoginPage/LoginPage";
import Console from "./screens/Console/Console";
import storage from "local-storage-fallback";
import { connect } from "react-redux";
import { AppState } from "./store";
import { userLoggedIn } from "./actions";
import LoginCallback from "./screens/LoginPage/LoginCallback";
import { hot } from "react-hot-loader/root";
import ProtectedRoute from "./ProtectedRoutes";
interface ProtectedRouteProps {
loggedIn: boolean;
component: any;
}
export class ProtectedRoute extends React.Component<ProtectedRouteProps> {
render() {
const Component = this.props.component;
return this.props.loggedIn ? (
<Component />
) : (
<Redirect to={{ pathname: "/login" }} />
);
}
}
const isLoggedIn = () => {
const Routes = () => {
return (
storage.getItem("token") !== undefined &&
storage.getItem("token") !== null &&
storage.getItem("token") !== ""
<Router history={history}>
<Switch>
<Route exact path="/oauth_callback" component={LoginCallback} />
<Route exact path="/login" component={Login} />
<ProtectedRoute Component={Console} />
</Switch>
</Router>
);
};
const mapState = (state: AppState) => ({
loggedIn: state.system.loggedIn,
});
const connector = connect(mapState, { userLoggedIn });
interface RoutesProps {
loggedIn: boolean;
userLoggedIn: typeof userLoggedIn;
}
class Routes extends React.Component<RoutesProps> {
render() {
const loggedIn = isLoggedIn();
return (
<Router history={history}>
<Switch>
<Route exact path="/oauth_callback" component={LoginCallback} />
<Route exact path="/login" component={Login} />
<ProtectedRoute component={Console} loggedIn={loggedIn} />
</Switch>
</Router>
);
}
}
export default hot(connector(Routes));
export default hot(Routes);

View File

@@ -14,16 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import storage from "local-storage-fallback";
import request from "superagent";
import get from "lodash/get";
import { clearSession } from "../utils";
export class API {
invoke(method: string, url: string, data?: object) {
const token: string = storage.getItem("token")!;
return request(method, url)
.set("Authorization", `Bearer ${token}`)
.send(data)
.then((res) => res.body)
.catch((err) => {

View File

@@ -62,17 +62,12 @@ export const deleteCookie = (name: string) => {
document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT;";
};
export const setSession = (token: string) => {
setCookie("token", token);
storage.setItem("token", token);
};
export const clearSession = () => {
storage.removeItem("token");
deleteCookie("token");
};
// timeFromdate gets time string from date input
// timeFromDate gets time string from date input
export const timeFromDate = (d: Date) => {
let h = d.getHours() < 10 ? `0${d.getHours()}` : `${d.getHours()}`;
let m = d.getMinutes() < 10 ? `0${d.getMinutes()}` : `${d.getMinutes()}`;

View File

@@ -0,0 +1,39 @@
// 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 from "react";
import { SvgIcon } from "@material-ui/core";
class CopyIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<title>ic_h_copy-new_sl</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"
/>
</g>
</g>
</svg>
</SvgIcon>
);
}
}
export default CopyIcon;

View File

@@ -15,21 +15,20 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import {SvgIcon} from "@material-ui/core";
import { SvgIcon } from "@material-ui/core";
class DeleteIcon extends React.Component {
render() {
return (<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<title>ic_h_delete</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path className="cls-1"
d="M0,8H0a8,8,0,0,0,8,8H8a8,8,0,0,0,8-8h0A8,8,0,0,0,8,0H8A8,8,0,0,0,0,8Zm10.007,3.489L8,9.482,5.993,11.489,4.511,10.007,6.518,8,4.511,5.993,5.993,4.511,8,6.518l2.007-2.007,1.482,1.482L9.482,8l2.007,2.007Z"/>
</g>
</g>
</svg>
</SvgIcon>)
}
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.402 13">
<path
d="M6.761 1V0H3.64v1H.004v1h10.4V1zM.004 2.998l1.672 10h7.052l1.673-10zm3.412 8.243l-.552-6.478h.653l.553 6.472zm3.569 0h-.653l.551-6.472h.654z"
className="a"
></path>
</svg>
</SvgIcon>
);
}
}
export default DeleteIcon;

View File

@@ -0,0 +1,33 @@
// 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 from "react";
import SvgIcon from "@material-ui/core/SvgIcon";
class DownloadIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<path d="M11.05 9.096v1.95h-9.1v-1.95H0v3.9h13v-3.9z"></path>
<path d="M6.5 9.75L9 6.672H7.475V0h-1.95v6.672H4z"></path>
</svg>
</SvgIcon>
);
}
}
export default DownloadIcon;

View File

@@ -0,0 +1,39 @@
// 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 from "react";
import SvgIcon from "@material-ui/core/SvgIcon";
class ShareIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 13">
<path
d="M11.05 8.617v2.429h-9.1v-9.1h2.429v-1.95H0v13h13V8.617z"
className="a"
></path>
<path
d="M3.854 9.256h1.95a4.945 4.945 0 013.6-4.74v1.3l.6-.487 2.474-2.012L9.4.817v1.7a6.9 6.9 0 00-5.546 6.739z"
className="a"
></path>
</svg>
</SvgIcon>
);
}
}
export default ShareIcon;

View File

@@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
export { default as PermissionIcon } from "./PermissionIcon";
export { default as CopyIcon } from "./CopyIcon";
export { default as CreateIcon } from "./CreateIcon";
export { default as DeleteIcon } from "./DeleteIcon";
export { default as ServiceAccountIcon } from "./ServiceAccountIcon";

View File

@@ -21,6 +21,7 @@ import Routes from "./Routes";
import configureStore from "./store";
import * as serviceWorker from "./serviceWorker";
import { ThemeProvider, withStyles } from "@material-ui/core/styles";
import "react-virtualized/styles.css";
import "./index.css";
import theme from "./theme/main";

View File

@@ -221,7 +221,7 @@ const AddBucket = ({
addBucketVersioned(event.target.checked);
}}
label={"Versioning"}
indicatorLabel={"On"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
<Grid item xs={12}>
@@ -234,7 +234,7 @@ const AddBucket = ({
addBucketQuota(event.target.checked);
}}
label={"Enable Bucket Quota"}
indicatorLabel={"On"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
{enableQuota && (

View File

@@ -197,7 +197,7 @@ const ListBuckets = ({
}
});
const showInPage = filteredRecords.slice(offset, offset + rowsPerPage);
const showInPage = filteredRecords;
return (
<React.Fragment>
@@ -266,26 +266,14 @@ const ListBuckets = ({
label: "Size",
elementKey: "size",
renderFunction: niceBytes,
width: 60,
contentTextAlign: "right",
},
]}
isLoading={loading}
records={showInPage}
entityName="Buckets"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -16,7 +16,7 @@
import React, { useState } from "react";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import { Button, Grid, LinearProgress } from "@material-ui/core";
import { Button, Grid } from "@material-ui/core";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";

View File

@@ -42,17 +42,22 @@ import Snackbar from "@material-ui/core/Snackbar";
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
import get from "lodash/get";
import { withRouter } from "react-router-dom";
import { addRoute, setAllRoutes } from "../../../../ObjectBrowser/actions";
import {
addRoute,
setAllRoutes,
setLastAsFile,
} from "../../../../ObjectBrowser/actions";
import { connect } from "react-redux";
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
import CreateFolderModal from "./CreateFolderModal";
import { create } from "domain";
import UploadFile from "../../../../../../icons/UploadFile";
import { download } from "../utils";
const commonIcon = {
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
width: 16,
minWidth: 16,
height: 40,
marginRight: 10,
};
@@ -91,6 +96,11 @@ const styles = (theme: Theme) =>
display: "flex",
alignItems: "center",
},
fileNameText: {
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
},
iconFolder: {
backgroundImage: "url(/images/ob_folder_clear.svg)",
...commonIcon,
@@ -104,11 +114,14 @@ const styles = (theme: Theme) =>
marginLeft: 10,
},
},
browsePaper: {
height: "calc(100vh - 280px)",
},
"@global": {
".rowElementRaw:hover .iconFileElm": {
".rowLine:hover .iconFileElm": {
backgroundImage: "url(/images/ob_file_filled.svg)",
},
".rowElementRaw:hover .iconFolderElm": {
".rowLine:hover .iconFolderElm": {
backgroundImage: "url(/images/ob_folder_filled.svg)",
},
},
@@ -121,9 +134,10 @@ const styles = (theme: Theme) =>
interface IListObjectsProps {
classes: any;
match: any;
addRoute: (param1: string, param2: string) => any;
addRoute: (param1: string, param2: string, param3: string) => any;
setAllRoutes: (path: string) => any;
routesList: Route[];
setLastAsFile: () => any;
}
interface ObjectBrowserReducer {
@@ -136,6 +150,7 @@ const ListObjects = ({
addRoute,
setAllRoutes,
routesList,
setLastAsFile,
}: IListObjectsProps) => {
const [records, setRecords] = useState<BucketObject[]>([]);
const [totalRecords, setTotalRecords] = useState<number>(0);
@@ -162,13 +177,16 @@ const ListObjects = ({
api
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
.then((res: BucketObjectsList) => {
setLoading(false);
setSelectedBucket(bucketName);
setRecords(res.objects || []);
setTotalRecords(!res.objects ? 0 : res.total);
setError("");
// TODO:
// if we get 0 results, and page > 0 , go down 1 page
// In case no objects were retrieved, We check if item is a file
if (!res.objects && extraPath !== "") {
verifyIfIsFile();
return;
}
setLoading(false);
})
.catch((err: any) => {
setLoading(false);
@@ -183,6 +201,30 @@ const ListObjects = ({
}
}, [match, routesList, setAllRoutes]);
const verifyIfIsFile = () => {
const bucketName = match.params["bucket"];
const internalPaths = match.params[0];
api
.invoke(
"GET",
`/api/v1/buckets/${bucketName}/objects?prefix=${internalPaths}`
)
.then((res: BucketObjectsList) => {
//It is a file since it has elements in the object, setting file flag and waiting for component mount
if (res.objects !== null) {
setLastAsFile();
} else {
// It is a folder, we remove loader
setLoading(false);
}
})
.catch((err: any) => {
setLoading(false);
setError(err);
});
};
const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
@@ -219,7 +261,6 @@ const ListObjects = ({
let xhr = new XMLHttpRequest();
xhr.open("POST", uploadUrl, true);
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
xhr.withCredentials = false;
xhr.onload = function (event) {
@@ -257,38 +298,6 @@ const ListObjects = ({
e.target.value = null;
};
const download = (bucketName: string, objectName: string) => {
const anchor = document.createElement("a");
document.body.appendChild(anchor);
const token: string = storage.getItem("token")!;
const xhr = new XMLHttpRequest();
xhr.open(
"GET",
`/api/v1/buckets/${bucketName}/objects/download?prefix=${objectName}`,
true
);
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
xhr.responseType = "blob";
xhr.onload = function (e) {
if (this.status === 200) {
const blob = new Blob([this.response], {
type: "octet/stream",
});
const blobUrl = window.URL.createObjectURL(blob);
anchor.href = blobUrl;
anchor.download = objectName;
anchor.click();
window.URL.revokeObjectURL(blobUrl);
anchor.remove();
}
};
xhr.send();
};
const displayParsedDate = (date: string) => {
return <reactMoment.default>{date}</reactMoment.default>;
};
@@ -313,13 +322,16 @@ const ListObjects = ({
const lastIndex = idElementClean.length - 1;
const newPath = `${currentPath}/${idElementClean[lastIndex]}`;
addRoute(newPath, idElementClean[lastIndex]);
addRoute(newPath, idElementClean[lastIndex], "path");
return;
}
// Element is a file. we open details here
// TODO: Add details open function here.
//console.log("object", idElementClean);
const pathInArray = idElement.split("/");
const fileName = pathInArray[pathInArray.length - 1];
const newPath = `${currentPath}/${fileName}`;
addRoute(newPath, fileName, "file");
return;
};
const uploadObject = (e: any): void => {
@@ -372,7 +384,9 @@ const ListObjects = ({
return (
<div className={classes.fileName}>
<div className={icon} />
<span>{splitItem[splitItem.length - 1]}</span>
<span className={classes.fileNameText}>
{splitItem[splitItem.length - 1]}
</span>
</div>
);
};
@@ -486,12 +500,15 @@ const ListObjects = ({
label: "Size",
elementKey: "size",
renderFunction: niceBytes,
width: 60,
contentTextAlign: "right",
},
]}
isLoading={loading}
entityName="Objects"
idField="name"
records={filteredRecords}
customPaperHeight={classes.browsePaper}
/>
</Grid>
</Grid>
@@ -507,6 +524,7 @@ const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
const mapDispatchToProps = {
addRoute,
setAllRoutes,
setLastAsFile,
};
const connector = connect(mapStateToProps, mapDispatchToProps);

View File

@@ -0,0 +1,68 @@
// 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 ListObjects from "./ListObjects";
import ObjectDetails from "../ObjectDetails/ObjectDetails";
import get from "lodash/get";
import { setAllRoutes } from "../../../../ObjectBrowser/actions";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
interface ObjectBrowserReducer {
objectBrowser: ObjectBrowserState;
}
interface ObjectRoutingProps {
routesList: Route[];
setAllRoutes: (path: string) => any;
match: any;
}
const ObjectRouting = ({
routesList,
match,
setAllRoutes,
}: ObjectRoutingProps) => {
const currentItem = routesList[routesList.length - 1];
useEffect(() => {
const url = get(match, "url", "/object-browser");
if (url !== routesList[routesList.length - 1].route) {
setAllRoutes(url);
}
}, [match, routesList, setAllRoutes]);
return currentItem.type === "path" ? (
<ListObjects />
) : (
<ObjectDetails routesList={routesList} />
);
};
const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
routesList: get(objectBrowser, "routesList", []),
});
const mapDispatchToProps = {
setAllRoutes,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
export default withRouter(connector(ObjectRouting));

View File

@@ -0,0 +1,138 @@
import React, { useState } from "react";
import { Button, Grid } from "@material-ui/core";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
import api from "../../../../../../common/api";
interface ITagModal {
modalOpen: boolean;
currentTags: any;
bucketName: string;
versionId: string;
onCloseAndUpdate: (refresh: boolean) => void;
selectedObject: string;
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
pathLabel: {
marginTop: 0,
marginBottom: 32,
},
...modalBasic,
});
const AddTagModal = ({
modalOpen,
currentTags,
selectedObject,
onCloseAndUpdate,
bucketName,
versionId,
classes,
}: ITagModal) => {
const [newKey, setNewKey] = useState<string>("");
const [newLabel, setNewLabel] = useState<string>("");
const [error, setError] = useState<string>("");
const [isSending, setIsSending] = useState<boolean>(false);
const resetForm = () => {
setNewLabel("");
setNewKey("");
};
const addTagProcess = () => {
setIsSending(true);
const newTag: any = {};
newTag[newKey] = newLabel;
const newTagList = { ...currentTags, ...newTag };
api
.invoke(
"PUT",
`/api/v1/buckets/${bucketName}/objects/tags?prefix=${selectedObject}&version_id=${versionId}`,
{ tags: newTagList }
)
.then((res: any) => {
setIsSending(false);
onCloseAndUpdate(true);
})
.catch((error) => {
setError(error);
setIsSending(false);
});
};
return (
<React.Fragment>
<ModalWrapper
modalOpen={modalOpen}
title="Add New Tag"
onClose={() => {
onCloseAndUpdate(false);
}}
>
<Grid container>
<h3 className={classes.pathLabel}>
Selected Object: {selectedObject}
</h3>
{error !== "" && <span>{error}</span>}
<Grid item xs={12}>
<InputBoxWrapper
value={newKey}
label={"New Tag Key"}
id={"newTagKey"}
name={"newTagKey"}
placeholder={"Enter New Tag Key"}
onChange={(e) => {
setNewKey(e.target.value);
}}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
value={newLabel}
label={"New Tag Label"}
id={"newTagLabel"}
name={"newTagLabel"}
placeholder={"Enter New Tag Label"}
onChange={(e) => {
setNewLabel(e.target.value);
}}
/>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={resetForm}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={
newLabel.trim() === "" || newKey.trim() === "" || isSending
}
onClick={addTagProcess}
>
Save
</Button>
</Grid>
</Grid>
</ModalWrapper>
</React.Fragment>
);
};
export default withStyles(styles)(AddTagModal);

View File

@@ -0,0 +1,125 @@
import React, { useState } from "react";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Grid,
LinearProgress,
} from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
import api from "../../../../../../common/api";
import Typography from "@material-ui/core/Typography";
interface IDeleteTagModal {
deleteOpen: boolean;
currentTags: any;
bucketName: string;
versionId: string;
selectedTag: [string, string];
onCloseAndUpdate: (refresh: boolean) => void;
selectedObject: string;
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
pathLabel: {
marginTop: 0,
marginBottom: 32,
},
...modalBasic,
});
const DeleteTagModal = ({
deleteOpen,
currentTags,
selectedObject,
selectedTag,
onCloseAndUpdate,
bucketName,
versionId,
classes,
}: IDeleteTagModal) => {
const [deleteError, setDeleteError] = useState<string>("");
const [deleteLoading, setDeleteSending] = useState<boolean>(false);
const [tagKey, tagLabel] = selectedTag;
const removeTagProcess = () => {
setDeleteSending(true);
const cleanObject = { ...currentTags };
delete cleanObject[tagKey];
api
.invoke(
"PUT",
`/api/v1/buckets/${bucketName}/objects/tags?prefix=${selectedObject}&version_id=${versionId}`,
{ tags: cleanObject }
)
.then((res: any) => {
setDeleteSending(false);
onCloseAndUpdate(true);
})
.catch((error) => {
setDeleteError(error);
setDeleteSending(false);
});
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
onCloseAndUpdate(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Tag</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete the tag{" "}
<b className={classes.wrapText}>
{tagKey} : {tagLabel}
</b>{" "}
from {selectedObject}?
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
onCloseAndUpdate(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button onClick={removeTagProcess} color="secondary" autoFocus>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeleteTagModal);

View File

@@ -0,0 +1,508 @@
// 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, { useState, useEffect } from "react";
import get from "lodash/get";
import * as reactMoment from "react-moment";
import clsx from "clsx";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Chip from "@material-ui/core/Chip";
import TextField from "@material-ui/core/TextField";
import IconButton from "@material-ui/core/IconButton";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import AddIcon from "@material-ui/icons/Add";
import CloseIcon from "@material-ui/icons/Close";
import ShareFile from "./ShareFile";
import {
actionsTray,
containerForHeader,
searchField,
} from "../../../../Common/FormComponents/common/styleLibrary";
import history from "../../../../../../history";
import { Route } from "../../../../ObjectBrowser/reducers";
import { download } from "../utils";
import api from "../../../../../../common/api";
import PageHeader from "../../../../Common/PageHeader/PageHeader";
import ShareIcon from "../../../../../../icons/ShareIcon";
import DownloadIcon from "../../../../../../icons/DownloadIcon";
import DeleteIcon from "../../../../../../icons/DeleteIcon";
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
import PencilIcon from "../../../../Common/TableWrapper/TableActionIcons/PencilIcon";
import SetRetention from "./SetRetention";
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
import DeleteObject from "../ListObjects/DeleteObject";
import { removeRouteLevel } from "../../../../ObjectBrowser/actions";
import { connect } from "react-redux";
import AddTagModal from "./AddTagModal";
import DeleteTagModal from "./DeleteTagModal";
const styles = (theme: Theme) =>
createStyles({
objectNameContainer: {
marginBottom: 8,
},
objectPathContainer: {
marginBottom: 26,
fontSize: 10,
},
objectPathLink: {
"&:visited": {
color: "#000",
},
},
objectName: {
fontSize: 24,
},
propertiesContainer: {
display: "flex",
flexDirection: "row",
marginBottom: 15,
},
propertiesItem: {
display: "flex",
flexDirection: "row",
marginRight: 21,
},
propertiesItemBold: {
fontWeight: 700,
},
propertiesValue: {
marginLeft: 8,
textTransform: "capitalize",
},
propertiesIcon: {
marginLeft: 5,
},
actionsIconContainer: {
marginLeft: 12,
},
actionsIcon: {
height: 16,
width: 16,
"& .MuiSvgIcon-root": {
height: 16,
},
},
tagsContainer: {
display: "flex",
flexDirection: "row",
alignItems: "center",
marginBottom: 15,
},
tagText: {
marginRight: 13,
},
tag: {
marginRight: 6,
fontSize: 10,
fontWeight: 700,
"&.MuiChip-sizeSmall": {
height: 18,
},
"& .MuiSvgIcon-root": {
height: 10,
width: 10,
},
},
search: {
marginBottom: 8,
"&.MuiFormControl-root": {
marginRight: 0,
},
},
...actionsTray,
...searchField,
...containerForHeader(theme.spacing(4)),
});
interface IObjectDetailsProps {
classes: any;
routesList: Route[];
removeRouteLevel: (newRoute: string) => any;
}
interface IFileInfo {
is_latest?: boolean;
last_modified: string;
legal_hold_status?: string;
name: string;
retention_mode?: string;
retention_until_date?: string;
size?: string;
tags?: object;
version_id: string;
}
const emptyFile: IFileInfo = {
is_latest: true,
last_modified: "",
legal_hold_status: "",
name: "",
retention_mode: "",
retention_until_date: "",
size: "0",
tags: {},
version_id: "",
};
const ObjectDetails = ({
classes,
routesList,
removeRouteLevel,
}: IObjectDetailsProps) => {
const [loadObjectData, setLoadObjectData] = useState<boolean>(true);
const [shareFileModalOpen, setShareFileModalOpen] = useState<boolean>(false);
const [retentionModalOpen, setRetentionModalOpen] = useState<boolean>(false);
const [tagModalOpen, setTagModalOpen] = useState<boolean>(false);
const [deleteTagModalOpen, setDeleteTagModalOpen] = useState<boolean>(false);
const [selectedTag, setSelectedTag] = useState<[string, string]>(["", ""]);
const [actualInfo, setActualInfo] = useState<IFileInfo>(emptyFile);
const [versions, setVersions] = useState<IFileInfo[]>([]);
const [filterVersion, setFilterVersion] = useState<string>("");
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const currentItem = routesList[routesList.length - 1];
const allPathData = currentItem.route.split("/");
const objectName = allPathData[allPathData.length - 1];
const bucketName = allPathData[2];
const pathInBucket = allPathData.slice(3).join("/");
useEffect(() => {
if (loadObjectData) {
api
.invoke(
"GET",
`/api/v1/buckets/${bucketName}/objects?prefix=${pathInBucket}&with_versions=true`
)
.then((res: IFileInfo[]) => {
const result = get(res, "objects", []);
setActualInfo(
result.find((el: IFileInfo) => el.is_latest) || emptyFile
);
setVersions(result.filter((el: IFileInfo) => !el.is_latest));
setLoadObjectData(false);
})
.catch((error) => {
setError(error);
setLoadObjectData(false);
});
}
}, [loadObjectData]);
let tagKeys: string[] = [];
if (actualInfo.tags) {
tagKeys = Object.keys(actualInfo.tags);
}
const openRetentionModal = () => {
setRetentionModalOpen(true);
console.log("open retention modal");
};
const closeRetentionModal = () => {
setRetentionModalOpen(false);
console.log("close retention modal");
};
const shareObject = () => {
setShareFileModalOpen(true);
console.log("share object");
};
const closeShareModal = () => {
setShareFileModalOpen(false);
console.log("close share modal");
};
const deleteTag = (tagKey: string, tagLabel: string) => {
setSelectedTag([tagKey, tagLabel]);
setDeleteTagModalOpen(true);
};
const downloadObject = (path: string) => {
download(bucketName, path);
};
const tableActions = [
{ type: "share", onClick: shareObject, sendOnlyId: true },
{ type: "download", onClick: downloadObject, sendOnlyId: true },
];
const filteredRecords = versions.filter((version) =>
version.version_id.includes(filterVersion)
);
const displayParsedDate = (date: string) => {
return <reactMoment.default>{date}</reactMoment.default>;
};
const closeDeleteModal = (redirectBack: boolean) => {
setDeleteOpen(false);
if (redirectBack) {
const newPath = allPathData.slice(0, -1).join("/");
removeRouteLevel(newPath);
history.push(newPath);
}
};
const closeAddTagModal = (reloadObjectData: boolean) => {
setTagModalOpen(false);
if (reloadObjectData) {
setLoadObjectData(true);
}
};
const closeDeleteTagModal = (reloadObjectData: boolean) => {
setDeleteTagModalOpen(false);
if (reloadObjectData) {
setLoadObjectData(true);
}
};
return (
<React.Fragment>
<PageHeader label={"Object Browser"} />
{shareFileModalOpen && (
<ShareFile
open={shareFileModalOpen}
closeModalAndRefresh={closeShareModal}
/>
)}
{retentionModalOpen && (
<SetRetention
open={retentionModalOpen}
closeModalAndRefresh={closeRetentionModal}
objectName={objectName}
/>
)}
{deleteOpen && (
<DeleteObject
deleteOpen={deleteOpen}
selectedBucket={bucketName}
selectedObject={pathInBucket}
closeDeleteModalAndRefresh={closeDeleteModal}
/>
)}
{tagModalOpen && (
<AddTagModal
modalOpen={tagModalOpen}
currentTags={actualInfo.tags}
selectedObject={pathInBucket}
versionId={actualInfo.version_id}
bucketName={bucketName}
onCloseAndUpdate={closeAddTagModal}
/>
)}
{deleteTagModalOpen && (
<DeleteTagModal
deleteOpen={deleteTagModalOpen}
currentTags={actualInfo.tags}
selectedObject={pathInBucket}
versionId={actualInfo.version_id}
bucketName={bucketName}
onCloseAndUpdate={closeDeleteTagModal}
selectedTag={selectedTag}
/>
)}
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.obTitleSection}>
<div>
<BrowserBreadcrumbs />
</div>
</Grid>
<br />
<Grid item xs={12} className={classes.propertiesContainer}>
<div className={classes.propertiesItem}>
<div>
<span className={classes.propertiesItemBold}>Legal Hold:</span>
<span className={classes.propertiesValue}>
{actualInfo.legal_hold_status
? actualInfo.legal_hold_status.toLowerCase()
: "Off"}
</span>
</div>
<div>
<IconButton
color="primary"
aria-label="legal-hold"
size="small"
className={classes.propertiesIcon}
onClick={() => {
console.log("open legal hold modal");
}}
>
<PencilIcon active={true} />
</IconButton>
</div>
</div>
<div className={classes.propertiesItem}>
<div>
<span className={classes.propertiesItemBold}>Retention:</span>
<span className={classes.propertiesValue}>
{actualInfo.retention_mode
? actualInfo.retention_mode.toLowerCase()
: "Undefined"}
</span>
</div>
<div>
<IconButton
color="primary"
aria-label="retention"
size="small"
className={classes.propertiesIcon}
onClick={() => {
openRetentionModal();
}}
>
<PencilIcon active={true} />
</IconButton>
</div>
</div>
<div className={classes.propertiesItem}>
<div className={classes.propertiesItemBold}>File Actions:</div>
<div className={classes.actionsIconContainer}>
<IconButton
color="primary"
aria-label="share"
size="small"
className={classes.actionsIcon}
onClick={() => {
shareObject();
}}
>
<ShareIcon />
</IconButton>
</div>
<div className={classes.actionsIconContainer}>
<IconButton
color="primary"
aria-label="download"
size="small"
className={classes.actionsIcon}
onClick={() => {
downloadObject(pathInBucket);
}}
>
<DownloadIcon />
</IconButton>
</div>
<div className={classes.actionsIconContainer}>
<IconButton
color="primary"
aria-label="delete"
size="small"
className={classes.actionsIcon}
onClick={() => {
setDeleteOpen(true);
}}
>
<DeleteIcon />
</IconButton>
</div>
</div>
</Grid>
<Grid item xs={12} className={classes.tagsContainer}>
<div className={classes.tagText}>Tags:</div>
{tagKeys.map((tagKey, index) => {
const tag = get(actualInfo, `tags.${tagKey}`, "");
if (tag !== "") {
return (
<Chip
key={`chip-${index}`}
className={classes.tag}
size="small"
label={`${tagKey} : ${tag}`}
color="primary"
deleteIcon={<CloseIcon />}
onDelete={() => {
deleteTag(tagKey, tag);
}}
/>
);
}
return null;
})}
<Chip
className={classes.tag}
icon={<AddIcon />}
clickable
size="small"
label="Add tag"
color="primary"
variant="outlined"
onClick={() => {
setTagModalOpen(true);
}}
/>
</Grid>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder={`Search ${objectName}`}
className={clsx(classes.search, classes.searchField)}
id="search-resource"
label=""
onChange={(val) => {
setFilterVersion(val.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "Version ID", elementKey: "version_id" },
{
label: "Last Modified",
elementKey: "last_modified",
renderFunction: displayParsedDate,
},
]}
isLoading={false}
entityName="Versions"
idField="version_id"
records={filteredRecords}
/>
</Grid>
</Grid>
</Grid>
</React.Fragment>
);
};
const mapDispatchToProps = {
removeRouteLevel,
};
const connector = connect(null, mapDispatchToProps);
export default connector(withStyles(styles)(ObjectDetails));

View File

@@ -0,0 +1,138 @@
import React, { useState, useRef } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector";
const styles = (theme: Theme) =>
createStyles({
objectName: {
fontSize: 18,
fontWeight: 700,
marginBottom: 40,
},
buttonContainer: {
textAlign: "right",
},
...modalBasic,
});
interface ISetRetentionProps {
classes: any;
open: boolean;
closeModalAndRefresh: () => void;
objectName: string;
}
interface IRefObject {
resetDate: () => void;
}
const SetRetention = ({
classes,
open,
closeModalAndRefresh,
objectName,
}: ISetRetentionProps) => {
const [statusEnabled, setStatusEnabled] = useState<boolean>(false);
const [type, setType] = useState<string>("");
const dateElement = useRef<IRefObject>(null);
const dateFieldDisabled = () => {
return !(statusEnabled && (type === "governance" || type === "compliance"));
};
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
};
const resetForm = () => {
setStatusEnabled(false);
setType("");
if (dateElement.current) {
dateElement.current.resetDate();
}
};
return (
<ModalWrapper
title="Set Retention Policy"
modalOpen={open}
onClose={() => {
resetForm();
closeModalAndRefresh();
}}
>
<Grid item xs={12} className={classes.objectName}>
{objectName}
</Grid>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
onSubmit(e);
}}
>
<Grid item xs={12}>
<FormSwitchWrapper
value="status"
id="status"
name="status"
checked={statusEnabled}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setStatusEnabled(!statusEnabled);
setType("");
}}
label={"Status"}
indicatorLabels={["Enabled", "Disabled"]}
/>
</Grid>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={type}
id="type"
name="type"
label="Type"
disableOptions={!statusEnabled}
onChange={(e) => {
setType(e.target.value);
}}
selectorOptions={[
{ label: "Governance", value: "governance" },
{ label: "Compliance", value: "compliance" },
]}
/>
</Grid>
<Grid item xs={12}>
<DateSelector
id="date"
label="Date"
disableOptions={dateFieldDisabled()}
ref={dateElement}
borderBottom={true}
/>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={resetForm}
>
Reset
</button>
<Button type="submit" variant="contained" color="primary">
Save
</Button>
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(SetRetention);

View File

@@ -0,0 +1,78 @@
import React, { useState } from "react";
import CopyToClipboard from "react-copy-to-clipboard";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import {
modalBasic,
predefinedList,
} from "../../../../Common/FormComponents/common/styleLibrary";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector";
import { CopyIcon } from "../../../../../../icons";
const styles = (theme: Theme) =>
createStyles({
copyButtonContainer: {
paddingLeft: 16,
},
modalContent: {
paddingBottom: 53,
},
...modalBasic,
...predefinedList,
});
interface IShareFileProps {
classes: any;
open: boolean;
closeModalAndRefresh: () => void;
}
const ShareFile = ({
classes,
open,
closeModalAndRefresh,
}: IShareFileProps) => {
return (
<ModalWrapper
title="Share File"
modalOpen={open}
onClose={() => {
closeModalAndRefresh();
}}
>
<Grid container className={classes.modalContent}>
<Grid item xs={12} className={classes.dateContainer}>
<DateSelector
id="date"
label="Active until"
borderBottom={false}
addSwitch={true}
/>
</Grid>
<Grid container item xs={12}>
<Grid item xs={10} className={classes.predefinedList}>
{"https://somelink.will/go/here"}
</Grid>
<Grid item xs={2} className={classes.copyButtonContainer}>
<CopyToClipboard text={"https://somelink.will/go/here"}>
<Button
variant="contained"
color="primary"
startIcon={<CopyIcon />}
onClick={() => {
console.log("copied!");
}}
>
Copy
</Button>
</CopyToClipboard>
</Grid>
</Grid>
</Grid>
</ModalWrapper>
);
};
export default withStyles(styles)(ShareFile);

View File

@@ -0,0 +1,48 @@
// 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 storage from "local-storage-fallback";
export const download = (bucketName: string, objectName: string) => {
const anchor = document.createElement("a");
document.body.appendChild(anchor);
const token: string = storage.getItem("token")!;
const xhr = new XMLHttpRequest();
xhr.open(
"GET",
`/api/v1/buckets/${bucketName}/objects/download?prefix=${objectName}`,
true
);
xhr.responseType = "blob";
xhr.onload = function (e) {
if (this.status === 200) {
const blob = new Blob([this.response], {
type: "octet/stream",
});
const blobUrl = window.URL.createObjectURL(blob);
anchor.href = blobUrl;
anchor.download = objectName;
anchor.click();
window.URL.revokeObjectURL(blobUrl);
anchor.remove();
}
};
xhr.send();
};

View File

@@ -642,20 +642,6 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
records={filteredRecords}
entityName="Events"
idField="id"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: totalRecords,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</TabPanel>
<TabPanel index={1} value={curTab}>
@@ -683,20 +669,6 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
records={filteredRules}
entityName="Replication Rules"
idField="id"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: totalRecords,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</TabPanel>
</Grid>

View File

@@ -82,14 +82,6 @@ const CredentialsPrompt = ({
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
A new {entity} has been created with the following details:
<ul>
<li>
<b>Access Key:</b> {newServiceAccount.accessKey}
</li>
<li>
<b>Secret Key:</b> {newServiceAccount.secretKey}
</li>
</ul>
{consoleCreds && (
<React.Fragment>
<Grid item xs={12}>
@@ -131,8 +123,6 @@ const CredentialsPrompt = ({
download(
"credentials.json",
JSON.stringify({
access_key: newServiceAccount.accessKey,
secret_key: newServiceAccount.secretKey,
...consoleExtras,
})
);

View File

@@ -15,8 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
export interface NewServiceAccount {
accessKey: string;
secretKey: string;
console?: ConsoleSA;
}

View File

@@ -0,0 +1,245 @@
import React, { useState, forwardRef, useImperativeHandle } from "react";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import InputLabel from "@material-ui/core/InputLabel";
import Tooltip from "@material-ui/core/Tooltip";
import HelpIcon from "@material-ui/icons/Help";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import InputBase from "@material-ui/core/InputBase";
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
import FormSwitchWrapper from "../FormSwitchWrapper/FormSwitchWrapper";
const styles = (theme: Theme) =>
createStyles({
dateInput: {
"&:not(:last-child)": {
marginRight: 22,
},
},
...fieldBasic,
...tooltipHelper,
labelContainer: {
flex: 1,
},
fieldContainer: {
...fieldBasic.fieldContainer,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
paddingBottom: 10,
marginTop: 11,
marginBottom: 6,
},
fieldContainerBorder: {
borderBottom: "#9c9c9c 1px solid",
marginBottom: 20,
},
});
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
root: {
"& .MuiSelect-icon": {
color: "#000",
"&.Mui-disabled": {
color: "#9c9c9c",
},
},
},
input: {
borderBottom: 0,
fontSize: 12,
},
})
)(InputBase);
interface IDateSelectorProps {
classes: any;
id: string;
label: string;
disableOptions?: boolean;
addSwitch?: boolean;
tooltip?: string;
borderBottom?: boolean;
}
const DateSelector = forwardRef(
(
{
classes,
id,
label,
disableOptions = false,
addSwitch = false,
tooltip = "",
borderBottom = false,
}: IDateSelectorProps,
ref: any
) => {
useImperativeHandle(ref, () => ({ resetDate }));
const [dateEnabled, setDateEnabled] = useState<boolean>(false);
const [month, setMonth] = useState<string>("");
const [day, setDay] = useState<string>("");
const [year, setYear] = useState<string>("");
const resetDate = () => {
setMonth("");
setDay("");
setYear("");
};
const isDateDisabled = () => {
if (disableOptions) {
return disableOptions;
} else if (addSwitch) {
return !dateEnabled;
} else {
return false;
}
};
const onMonthChange = (
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
) => {
setMonth(e.target.value as string);
};
const onDayChange = (
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
) => {
setDay(e.target.value as string);
};
const onYearChange = (
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
) => {
setYear(e.target.value as string);
};
return (
<Grid
item
xs={12}
className={clsx(classes.fieldContainer, {
[classes.fieldContainerBorder]: borderBottom,
})}
>
<div className={classes.labelContainer}>
<Grid container>
<InputLabel htmlFor={id} className={classes.inputLabel}>
<span>{label}</span>
{tooltip !== "" && (
<div className={classes.tooltipContainer}>
<Tooltip title={tooltip} placement="top-start">
<HelpIcon className={classes.tooltip} />
</Tooltip>
</div>
)}
</InputLabel>
{addSwitch && (
<FormSwitchWrapper
indicatorLabels={["Specific Date", "Always active"]}
checked={dateEnabled}
value={"date_enabled"}
id="date-status"
name="date-status"
onChange={(e) => {
setDateEnabled(e.target.checked);
}}
switchOnly
/>
)}
</Grid>
</div>
<div>
<FormControl
disabled={isDateDisabled()}
className={classes.dateInput}
>
<Select
id={`${id}-month`}
name={`${id}-month`}
value={month}
displayEmpty
onChange={onMonthChange}
input={<SelectStyled />}
>
<MenuItem value="" disabled>
{"<Month>"}
</MenuItem>
<MenuItem value={"1"}>January</MenuItem>
{/* {options.map((option) => (
<MenuItem
value={option.value}
key={`select-${name}-${option.label}`}
>
{option.label}
</MenuItem>
))} */}
</Select>
</FormControl>
<FormControl
disabled={isDateDisabled()}
className={classes.dateInput}
>
<Select
id={`${id}-day`}
name={`${id}-day`}
value={day}
displayEmpty
onChange={onDayChange}
input={<SelectStyled />}
>
<MenuItem value="" disabled>
{"<Day>"}
</MenuItem>
<MenuItem value={"1"}>1</MenuItem>
<MenuItem value={"2"}>2</MenuItem>
{/* {options.map((option) => (
<MenuItem
value={option.value}
key={`select-${name}-${option.label}`}
>
{option.label}
</MenuItem>
))} */}
</Select>
</FormControl>
<FormControl
disabled={isDateDisabled()}
className={classes.dateInput}
>
<Select
id={`${id}-year`}
name={`${id}-year`}
value={year}
displayEmpty
onChange={onYearChange}
input={<SelectStyled />}
>
<MenuItem value="" disabled>
{"<Year>"}
</MenuItem>
<MenuItem value={"2020"}>2020</MenuItem>
<MenuItem value={"2021"}>2021</MenuItem>
{/* {options.map((option) => (
<MenuItem
value={option.value}
key={`select-${name}-${option.label}`}
>
{option.label}
</MenuItem>
))} */}
</Select>
</FormControl>
</div>
</Grid>
);
}
);
export default withStyles(styles)(DateSelector);

View File

@@ -31,7 +31,7 @@ interface IFormSwitch {
disabled?: boolean;
tooltip?: string;
index?: number;
indicatorLabel?: string;
indicatorLabels?: string[];
checked: boolean;
switchOnly?: boolean;
}
@@ -142,14 +142,21 @@ const StyledSwitch = withStyles({
opacity: 1,
height: 15,
},
"&:hover": {
backgroundColor: "#fff",
},
},
checked: {},
track: {
height: 15,
backgroundColor: "#081C42",
backgroundColor: "#9C9C9C",
border: "#081C42 1px solid",
opacity: 1,
padding: 0,
marginTop: 1.5,
"&$checked": {
backgroundColor: "#081C42",
},
},
thumb: {
backgroundColor: "#fff",
@@ -172,7 +179,7 @@ const FormSwitchWrapper = ({
disabled = false,
switchOnly = false,
tooltip = "",
indicatorLabel = "",
indicatorLabels = [],
classes,
}: IFormSwitch) => {
const switchComponent = (
@@ -190,8 +197,10 @@ const FormSwitchWrapper = ({
disableTouchRipple
value={value}
/>
{indicatorLabel !== "" && (
<span className={classes.indicatorLabel}>{indicatorLabel}</span>
{indicatorLabels.length === 2 && (
<span className={classes.indicatorLabel}>
{checked ? indicatorLabels[0] : indicatorLabels[1]}
</span>
)}
</div>
</React.Fragment>

View File

@@ -14,6 +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 from "react";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";
import RadioGroup from "@material-ui/core/RadioGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
@@ -40,6 +41,7 @@ interface RadioGroupProps {
id: string;
name: string;
tooltip?: string;
disableOptions?: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
classes: any;
displayInColumn?: boolean;
@@ -58,8 +60,23 @@ const styles = (theme: Theme) =>
paddingBottom: 10,
marginTop: 11,
},
optionLabel: {
"&.Mui-disabled": {
"& .MuiFormControlLabel-label": {
color: "#9c9c9c",
},
},
"&:last-child": {
marginRight: 0,
},
"& .MuiFormControlLabel-label": {
fontSize: 12,
color: "#000",
},
},
checkedOption: {
"& .MuiFormControlLabel-label": {
fontSize: 12,
color: "#000",
fontWeight: 700,
},
@@ -98,6 +115,7 @@ export const RadioGroupSelector = ({
name,
onChange,
tooltip = "",
disableOptions = false,
classes,
displayInColumn = false,
}: RadioGroupProps) => {
@@ -128,14 +146,14 @@ export const RadioGroupSelector = ({
return (
<FormControlLabel
key={`rd-${name}-${selectorOption.value}`}
value={selectorOption.value}
value={disableOptions ? "disabled" : selectorOption.value}
control={<RadioButton />}
label={selectorOption.label}
className={
selectorOption.value === currentSelection
? classes.checkedOption
: ""
}
disabled={disableOptions}
className={clsx(classes.optionLabel, {
[classes.checkedOption]:
selectorOption.value === currentSelection,
})}
/>
);
})}

View File

@@ -54,6 +54,7 @@ export const modalBasic = {
marginLeft: 0,
},
clearButton: {
fontFamily: "Lato, sans-serif",
border: "0",
backgroundColor: "transparent",
color: "#393939",
@@ -104,6 +105,9 @@ const radioBasic = {
width: 12,
height: 12,
borderRadius: "100%",
"input:disabled ~ &": {
border: "1px solid #9C9C9C",
},
};
export const radioIcons = {
@@ -117,8 +121,7 @@ export const radioIcons = {
export const containerForHeader = (bottomSpacing: any) => ({
container: {
padding: "110px 33px 30px",
paddingBottom: bottomSpacing,
padding: "110px 33px 0",
"& h6": {
color: "#777777",
fontSize: 14,
@@ -153,13 +156,17 @@ export const searchField = {
justifyContent: "center",
padding: "0 16px",
"& input": {
fontSize: 14,
fontSize: 12,
fontWeight: 700,
color: "#000",
"&::placeholder": {
color: "#393939",
opacity: 1,
},
},
"&:hover": {
borderColor: "#000",
},
},
};
@@ -207,3 +214,9 @@ export const objectBrowserCommon = {
},
},
};
export const selectorsCommon = {
multiSelectTable: {
height: 200,
},
};

View File

@@ -18,13 +18,21 @@ import isString from "lodash/isString";
import { IconButton } from "@material-ui/core";
import ViewIcon from "./TableActionIcons/ViewIcon";
import PencilIcon from "./TableActionIcons/PencilIcon";
import ShareIcon from "./TableActionIcons/ShareIcon";
import DeleteIcon from "./TableActionIcons/DeleteIcon";
import DescriptionIcon from "./TableActionIcons/DescriptionIcon";
import CloudIcon from "./TableActionIcons/CloudIcon";
import ConsoleIcon from "./TableActionIcons/ConsoleIcon";
import GetAppIcon from "@material-ui/icons/GetApp";
import SvgIcon from "@material-ui/core/SvgIcon";
import DownloadIcon from "./TableActionIcons/DownloadIcon";
import { Link } from "react-router-dom";
import { createStyles, withStyles } from "@material-ui/core/styles";
const styles = () =>
createStyles({
spacing: {
margin: "0 8px",
},
});
interface IActionButton {
type: string;
@@ -34,6 +42,7 @@ interface IActionButton {
selected: boolean;
sendOnlyId?: boolean;
idField: string;
classes: any;
}
const defineIcon = (type: string, selected: boolean) => {
@@ -46,14 +55,14 @@ const defineIcon = (type: string, selected: boolean) => {
return <DeleteIcon active={selected} />;
case "description":
return <DescriptionIcon active={selected} />;
case "share":
return <ShareIcon active={selected} />;
case "cloud":
return <CloudIcon active={selected} />;
case "console":
return <ConsoleIcon active={selected} />;
case "download":
return (
<SvgIcon component={GetAppIcon} fontSize="small" color="primary" />
);
return <DownloadIcon active={selected} />;
}
return null;
@@ -67,6 +76,7 @@ const TableActionButton = ({
selected,
to,
sendOnlyId = false,
classes,
}: IActionButton) => {
const valueClick = sendOnlyId ? valueToSend[idField] : valueToSend;
@@ -82,6 +92,7 @@ const TableActionButton = ({
}
: () => null
}
className={classes.spacing}
>
{defineIcon(type, selected)}
</IconButton>
@@ -107,4 +118,4 @@ const TableActionButton = ({
return null;
};
export default TableActionButton;
export default withStyles(styles)(TableActionButton);

View File

@@ -9,17 +9,10 @@ const DeleteIcon = ({ active = false }: IIcon) => {
height="16"
viewBox="0 0 10.402 13"
>
<g transform="translate(0.004 -28.959)">
<path
fill={active ? selected : unSelected}
d="M6.757,29.959v-1H3.636v1H0v1H10.4v-1Z"
/>
<path
fill={active ? selected : unSelected}
d="M0,31.957l1.672,10H8.724l1.673-10ZM3.412,40.2,2.86,33.722h.653l.553,6.472Zm3.569,0H6.328l.551-6.472h.654Z"
transform="translate(0 0)"
/>
</g>
<path
fill={active ? selected : unSelected}
d="M6.761 1V0H3.64v1H.004v1h10.4V1zM.004 2.998l1.672 10h7.052l1.673-10zm3.412 8.243l-.552-6.478h.653l.553 6.472zm3.569 0h-.653l.551-6.472h.654z"
/>
</svg>
);
};

View File

@@ -0,0 +1,24 @@
import React from "react";
import { IIcon, selected, unSelected } from "./common";
const DeleteIcon = ({ active = false }: IIcon) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 13 12.996"
>
<path
fill={active ? selected : unSelected}
d="M11.05 9.096v1.95h-9.1v-1.95H0v3.9h13v-3.9z"
></path>
<path
fill={active ? selected : unSelected}
d="M6.5 9.75L9 6.672H7.475V0h-1.95v6.672H4z"
></path>
</svg>
);
};
export default DeleteIcon;

View File

@@ -0,0 +1,26 @@
import React from "react";
import { IIcon, selected, unSelected } from "./common";
const ShareIcon = ({ active = false }: IIcon) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 13 13"
>
<path
fill={active ? selected : unSelected}
d="M11.05 8.617v2.429h-9.1v-9.1h2.429v-1.95H0v13h13V8.617z"
className="a"
></path>
<path
fill={active ? selected : unSelected}
d="M3.854 9.256h1.95a4.945 4.945 0 013.6-4.74v1.3l.6-.487 2.474-2.012L9.4.817v1.7a6.9 6.9 0 00-5.546 6.739z"
className="a"
></path>
</svg>
);
};
export default ShareIcon;

View File

@@ -18,19 +18,13 @@ import get from "lodash/get";
import isString from "lodash/isString";
import {
LinearProgress,
TablePagination,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Grid,
Checkbox,
Typography,
} from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions";
import { Table, Column, AutoSizer } from "react-virtualized";
import { createStyles, withStyles } from "@material-ui/core/styles";
import TableActionButton from "./TableActionButton";
import history from "../../../../history";
import {
@@ -50,28 +44,13 @@ interface ItemActions {
interface IColumns {
label: string;
elementKey: string;
sortable?: boolean;
renderFunction?: (input: any) => any;
renderFullObject?: boolean;
globalClass?: any;
rowClass?: any;
}
interface IPaginatorConfig {
rowsPerPageOptions: number[];
colSpan: number;
count: number;
rowsPerPage: number;
page: number;
SelectProps: any;
onChangePage: (
event: React.MouseEvent<HTMLButtonElement> | null,
page: number
) => void;
onChangeRowsPerPage?: React.ChangeEventHandler<
HTMLTextAreaElement | HTMLInputElement
>;
ActionsComponent?: React.ElementType<TablePaginationActionsProps>;
width?: number;
headerTextAlign?: string;
contentTextAlign?: string;
}
interface TableWrapperProps {
@@ -84,10 +63,9 @@ interface TableWrapperProps {
classes: any;
entityName: string;
selectedItems?: string[];
stickyHeader?: boolean;
radioSelection?: boolean;
customEmptyMessage?: string;
paginatorConfig?: IPaginatorConfig;
customPaperHeight?: string;
}
const borderColor = "#9c9c9c80";
@@ -104,7 +82,7 @@ const rowText = {
paddingLeft: 6,
};
const styles = (theme: Theme) =>
const styles = () =>
createStyles({
dialogContainer: {
padding: "12px 26px 22px",
@@ -117,7 +95,16 @@ const styles = (theme: Theme) =>
boxShadow: "none",
border: "#EAEDEE 1px solid",
borderRadius: 3,
minHeight: "calc(100vh - 340px)",
minHeight: 200,
overflowY: "scroll",
"&::-webkit-scrollbar": {
width: 3,
height: 3,
},
},
defaultPaperHeight: {
height: "calc(100vh - 205px)",
},
allTableSettings: {
"& .MuiTableCell-sizeSmall:last-child": {
@@ -177,63 +164,143 @@ const styles = (theme: Theme) =>
paddingTop: "100px",
paddingBottom: "100px",
},
rowElement: {
userSelect: "none",
"&:hover": {
backgroundColor: "#ececec",
"& td": {
"@global": {
".rowLine": {
borderBottom: `1px solid ${borderColor}`,
height: 40,
color: "#393939",
fontSize: 14,
transitionDuration: 0.3,
"&:focus": {
outline: "initial",
},
"&:hover:not(.ReactVirtualized__Table__headerRow)": {
userSelect: "none",
backgroundColor: "#ececec",
fontWeight: 600,
"&.canClick": {
cursor: "pointer",
},
},
"& .selected": {
color: "#081C42",
fontWeight: 600,
},
},
},
rowClickable: {
cursor: "pointer",
".headerItem": {
userSelect: "none",
fontWeight: 700,
fontSize: 14,
fontStyle: "initial",
},
".ReactVirtualized__Table__headerRow": {
fontWeight: 700,
fontSize: 14,
borderColor: "#39393980",
textTransform: "initial",
},
".optionsAlignment": {
textAlign: "center",
},
".text-center": {
textAlign: "center",
},
".text-right": {
textAlign: "right",
},
},
...checkboxIcons,
...radioIcons,
});
// Function that renders Title Columns
const titleColumnsMap = (columns: IColumns[]) => {
return columns.map((column: IColumns, index: number) => {
return (
<TableCell
key={`tbCT-${column.elementKey}-${index}`}
className={column.globalClass}
>
{column.label}
</TableCell>
);
});
};
const selectWidth = 45;
// Function that renders Rows
const rowColumnsMap = (
columns: IColumns[],
itemData: any,
classes: any,
// Function to render elements in table
const subRenderFunction = (
rowData: any,
column: IColumns,
isSelected: boolean
) => {
return columns.map((column: IColumns, index: number) => {
const itemElement = isString(itemData)
? itemData
: get(itemData, column.elementKey, null); // If the element is just a string, we render it as it is
const renderConst = column.renderFullObject ? itemData : itemElement;
const itemElement = isString(rowData)
? rowData
: get(rowData, column.elementKey, null); // If the element is just a string, we render it as it is
const renderConst = column.renderFullObject ? rowData : itemElement;
const renderElement = column.renderFunction
? column.renderFunction(renderConst)
: renderConst; // If render function is set, we send the value to the function.
const renderElement = column.renderFunction
? column.renderFunction(renderConst)
: renderConst; // If render function is set, we send the value to the function.
return (
<React.Fragment>
<span className={isSelected ? "selected" : ""}>{renderElement}</span>
</React.Fragment>
);
};
// Function to calculate common column width for elements with no with size
const calculateColumnRest = (
columns: IColumns[],
containerWidth: number,
actionsWidth: number,
hasSelect: boolean,
hasActions: boolean
) => {
let initialValue = containerWidth;
if (hasSelect) {
initialValue -= selectWidth;
}
if (hasActions) {
initialValue -= actionsWidth;
}
let freeSpacing = columns.reduce((total, currValue) => {
return currValue.width ? total - currValue.width : total;
}, initialValue);
return freeSpacing / columns.filter((el) => !el.width).length;
};
// Function that renders Columns in table
const generateColumnsMap = (
columns: IColumns[],
containerWidth: number,
actionsWidth: number,
hasSelect: boolean,
hasActions: boolean,
selectedItems: string[],
idField: string
) => {
const commonRestWidth = calculateColumnRest(
columns,
containerWidth,
actionsWidth,
hasSelect,
hasActions
);
return columns.map((column: IColumns, index: number) => {
return (
<TableCell
key={`tbRE-${column.elementKey}-${index}`}
className={`${column.rowClass} ${
isSelected ? classes.rowSelected : classes.rowUnselected
<Column
key={`col-tb-${index.toString()}`}
dataKey={column.elementKey}
headerClassName={`titleHeader ${
column.headerTextAlign ? `text-${column.headerTextAlign}` : ""
}`}
>
{renderElement}
</TableCell>
headerRenderer={() => <React.Fragment>{column.label}</React.Fragment>}
className={
column.contentTextAlign ? `text-${column.contentTextAlign}` : ""
}
cellRenderer={({ rowData }) => {
const isSelected = selectedItems
? selectedItems.includes(
isString(rowData) ? rowData : rowData[idField]
)
: false;
return subRenderFunction(rowData, column, isSelected);
}}
width={column.width || commonRestWidth}
/>
);
});
};
@@ -265,6 +332,22 @@ const elementActions = (
});
};
// Function to calculate the options column width according elements inside
const calculateOptionsSize = (containerWidth: number, totalOptions: number) => {
const minContainerSize = 80;
const sizeOptions = totalOptions * 45;
if (sizeOptions < minContainerSize) {
return minContainerSize;
}
if (sizeOptions > containerWidth) {
return containerWidth;
}
return sizeOptions;
};
// Main function to render the Table Wrapper
const TableWrapper = ({
itemActions,
@@ -276,10 +359,9 @@ const TableWrapper = ({
selectedItems,
idField,
classes,
stickyHeader = false,
radioSelection = false,
customEmptyMessage = "",
paginatorConfig,
customPaperHeight = "",
}: TableWrapperProps) => {
const findView = itemActions
? itemActions.find((el) => el.type === "view")
@@ -301,7 +383,13 @@ const TableWrapper = ({
return (
<Grid item xs={12}>
<Paper className={classes.paper}>
<Paper
className={`${classes.paper} ${
customPaperHeight !== ""
? customPaperHeight
: classes.defaultPaperHeight
}`}
>
{isLoading && (
<Grid container className={classes.loadingBox}>
<Grid item xs={12} style={{ textAlign: "center" }}>
@@ -313,105 +401,133 @@ const TableWrapper = ({
</Grid>
)}
{records && !isLoading && records.length > 0 ? (
<Table
size="small"
stickyHeader={stickyHeader}
className={classes.allTableSettings}
>
<TableHead className={classes.minTableHeader}>
<TableRow>
{onSelect && selectedItems && (
<TableCell align="center" className={classes.checkBoxHeader}>
Select
</TableCell>
)}
{titleColumnsMap(columns)}
{((itemActions && itemActions.length > 1) ||
(itemActions &&
itemActions.length === 1 &&
itemActions[0].type !== "view")) && (
<TableCell
align="center"
className={classes.actionsContainer}
>
Actions
</TableCell>
)}
</TableRow>
</TableHead>
<TableBody>
{records.map((record: any, index: number) => {
const isSelected = selectedItems
? selectedItems.includes(
isString(record) ? record : record[idField]
)
: false;
<AutoSizer>
{({ width, height }: any) => {
const optionsWidth = calculateOptionsSize(
width,
itemActions
? itemActions.filter((el) => el.type !== "view").length
: 0
);
const hasSelect: boolean = !!(onSelect && selectedItems);
const hasOptions: boolean = !!(
(itemActions && itemActions.length > 1) ||
(itemActions &&
itemActions.length === 1 &&
itemActions[0].type !== "view")
);
return (
<Table
ref="Table"
disableHeader={false}
headerClassName={"headerItem"}
headerHeight={40}
height={height}
noRowsRenderer={() => (
<React.Fragment>
{customEmptyMessage !== ""
? customEmptyMessage
: `There are no ${entityName} yet.`}
</React.Fragment>
)}
overscanRowCount={10}
rowHeight={40}
width={width}
rowCount={records.length}
rowGetter={({ index }) => records[index]}
onRowClick={({ rowData }) => {
clickAction(rowData);
}}
rowClassName={`rowLine ${findView ? "canClick" : ""}`}
>
{hasSelect && (
<Column
headerRenderer={() => (
<React.Fragment>Select</React.Fragment>
)}
dataKey={idField}
width={selectWidth}
cellRenderer={({ rowData }) => {
const isSelected = selectedItems
? selectedItems.includes(
isString(rowData) ? rowData : rowData[idField]
)
: false;
return (
<TableRow
key={`tb-${entityName}-${index.toString()}`}
className={`${findView ? classes.rowClickable : ""} ${
classes.rowElement
} rowElementRaw`}
onClick={() => {
clickAction(record);
}}
>
{onSelect && selectedItems && (
<TableCell align="center" className={classes.checkBoxRow}>
<Checkbox
value={isString(record) ? record : record[idField]}
color="primary"
inputProps={{ "aria-label": "secondary checkbox" }}
checked={isSelected}
onChange={onSelect}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
checkedIcon={
<span
className={
radioSelection
? classes.radioSelectedIcon
: classes.checkedIcon
}
/>
}
icon={
<span
className={
radioSelection
? classes.radioUnselectedIcon
: classes.unCheckedIcon
}
/>
}
/>
</TableCell>
)}
{rowColumnsMap(columns, record, classes, isSelected)}
{((itemActions && itemActions.length > 1) ||
(itemActions &&
itemActions.length === 1 &&
itemActions[0].type !== "view")) && (
<TableCell
align="center"
className={classes.actionsContainer}
>
{elementActions(
itemActions,
record,
return (
<Checkbox
value={
isString(rowData) ? rowData : rowData[idField]
}
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
checked={isSelected}
onChange={onSelect}
onClick={(e) => {
e.stopPropagation();
}}
checkedIcon={
<span
className={
radioSelection
? classes.radioSelectedIcon
: classes.checkedIcon
}
/>
}
icon={
<span
className={
radioSelection
? classes.radioUnselectedIcon
: classes.unCheckedIcon
}
/>
}
/>
);
}}
/>
)}
{generateColumnsMap(
columns,
width,
optionsWidth,
hasSelect,
hasOptions,
selectedItems || [],
idField
)}
{hasOptions && (
<Column
headerRenderer={() => (
<React.Fragment>Options</React.Fragment>
)}
dataKey={idField}
width={optionsWidth}
headerClassName="optionsAlignment"
className="optionsAlignment"
cellRenderer={({ rowData }) => {
const isSelected = selectedItems
? selectedItems.includes(
isString(rowData) ? rowData : rowData[idField]
)
: false;
return elementActions(
itemActions || [],
rowData,
isSelected,
idField
)}
</TableCell>
)}
</TableRow>
);
})}
</TableBody>
</Table>
);
}}
/>
)}
</Table>
);
}}
</AutoSizer>
) : (
<React.Fragment>
{!isLoading && (
@@ -424,20 +540,6 @@ const TableWrapper = ({
</React.Fragment>
)}
</Paper>
{paginatorConfig && (
<Grid item xs={12} className={classes.paginatorContainer}>
<Table>
<TableBody>
<TableRow>
<TablePagination
{...paginatorConfig}
className={classes.paginatorComponent}
/>
</TableRow>
</TableBody>
</Table>
</Grid>
)}
</Grid>
);
};

View File

@@ -100,7 +100,7 @@ const ConfTargetGeneric = ({
return (
<FormSwitchWrapper
indicatorLabel="On"
indicatorLabels={["On", "Off"]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.checked ? "true" : "false";
setValueElement(field.name, value, item);

View File

@@ -143,7 +143,7 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
name="checkedB"
onChange={switcherChangeEvt}
value={"dnsString"}
indicatorLabel={"On"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
{useDsnString ? (

View File

@@ -216,7 +216,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
setUseConnectionString(e.target.checked);
}}
value={"manualString"}
indicatorLabel={"On"}
indicatorLabels={["On", "Off"]}
/>
</Grid>
{useConnectionString ? (

View File

@@ -46,11 +46,11 @@ import Heal from "./Heal/Heal";
import Watch from "./Watch/Watch";
import ListTenants from "./Tenants/ListTenants/ListTenants";
import { ISessionResponse } from "./types";
import { saveSessionResponse } from "./actions";
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
import { clearSession } from "../../common/utils";
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
import ListObjects from "./Buckets/ListBuckets/Objects/ListObjects/ListObjects";
import ObjectDetails from "./Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails";
import ObjectRouting from "./Buckets/ListBuckets/Objects/ListObjects/ObjectRouting";
import License from "./License/License";
const drawerWidth = 245;
@@ -160,7 +160,6 @@ interface IConsoleProps {
setMenuOpen: typeof setMenuOpen;
serverNeedsRestart: typeof serverNeedsRestart;
serverIsLoading: typeof serverIsLoading;
saveSessionResponse: typeof saveSessionResponse;
session: ISessionResponse;
}
@@ -171,24 +170,8 @@ const Console = ({
isServerLoading,
serverNeedsRestart,
serverIsLoading,
saveSessionResponse,
session,
}: IConsoleProps) => {
useEffect(() => {
api
.invoke("GET", `/api/v1/session`)
.then((res) => {
saveSessionResponse(res);
})
.catch(() => {
// if server returns 401 for /api/v1/session call invoke function will internally call clearSession()
// and redirecto to window.location.href = "/"; and this code will be not reached
// in case that not happen we clear session here and redirect as well
clearSession();
window.location.href = "/login";
});
}, [saveSessionResponse]);
const restartServer = () => {
serverIsLoading(true);
api
@@ -231,11 +214,11 @@ const Console = ({
path: "/object-browser",
},
{
component: ListObjects,
component: ObjectRouting,
path: "/object-browser/:bucket",
},
{
component: ListObjects,
component: ObjectRouting,
path: "/object-browser/:bucket/*",
},
{
@@ -375,7 +358,6 @@ const connector = connect(mapState, {
setMenuOpen,
serverNeedsRestart,
serverIsLoading,
saveSessionResponse,
});
export default connector(withStyles(styles)(Console));

View File

@@ -179,7 +179,7 @@ const AddGroup = ({
{selectedGroup !== null && (
<div className={classes.floatingEnabled}>
<FormSwitchWrapper
indicatorLabel={"Enabled"}
indicatorLabels={["Enabled", "Disabled"]}
checked={groupEnabled}
value={"group_enabled"}
id="group-status"

View File

@@ -257,20 +257,6 @@ const Groups = ({ classes }: IGroupsProps) => {
records={filteredRecords}
entityName="Groups"
idField=""
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: totalRecords,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -27,7 +27,10 @@ import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
import {
actionsTray,
selectorsCommon,
} from "../Common/FormComponents/common/styleLibrary";
interface IGroupsProps {
classes: any;
@@ -102,6 +105,7 @@ const styles = (theme: Theme) =>
},
},
...actionsTray,
...selectorsCommon,
});
const UsersSelectors = ({
@@ -211,6 +215,7 @@ const UsersSelectors = ({
records={filteredRecords}
entityName="Users"
idField="accessKey"
customPaperHeight={classes.multiSelectTable}
/>
</Grid>
</React.Fragment>

View File

@@ -205,27 +205,6 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
records={filteredRecords}
entityName="Notification Endpoints"
idField="service_name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: totalRecords,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: (event: unknown, newPage: number) => {
setPage(newPage);
},
onChangeRowsPerPage: (
event: React.ChangeEvent<HTMLInputElement>
) => {
const rPP = parseInt(event.target.value, 10);
setRowsPerPage(rPP);
},
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -90,10 +90,13 @@ const styles = (theme: Theme) =>
marginRight: 10,
},
"@global": {
".rowElementRaw:hover .iconBucketElm": {
".rowLine:hover .iconBucketElm": {
backgroundImage: "url(/images/ob_bucket_filled.svg)",
},
},
browsePaper: {
height: "calc(100vh - 280px)",
},
...actionsTray,
...searchField,
...objectBrowserCommon,
@@ -101,7 +104,7 @@ const styles = (theme: Theme) =>
interface IBrowseBucketsProps {
classes: any;
addRoute: (path: string, label: string) => any;
addRoute: (path: string, label: string, type: string) => any;
resetRoutesList: (doVar: boolean) => any;
match: any;
}
@@ -131,10 +134,8 @@ const BrowseBuckets = ({
api
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
.then((res: BucketList) => {
const buckets = get(res, "buckets", []);
setLoading(false);
setRecords(buckets);
setRecords(res.buckets || []);
setError("");
// if we get 0 results, and page > 0 , go down 1 page
if (
@@ -170,8 +171,6 @@ const BrowseBuckets = ({
return b.name.indexOf(filterBuckets) >= 0;
});
const showInPage = filteredRecords.slice(offset, offset + rowsPerPage);
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
@@ -188,7 +187,7 @@ const BrowseBuckets = ({
const currentPath = get(match, "url", "/object-browser");
const newPath = `${currentPath}/${idElement}`;
addRoute(newPath, idElement);
addRoute(newPath, idElement, "path");
};
const renderBucket = (bucketName: string) => {
@@ -270,26 +269,16 @@ const BrowseBuckets = ({
renderFunction: niceBytes,
globalClass: classes.usedSpaceCol,
rowClass: classes.usedSpaceCol,
width: 100,
contentTextAlign: "right",
headerTextAlign: "right",
},
]}
isLoading={loading}
records={showInPage}
records={filteredRecords}
entityName="Buckets"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
customPaperHeight={classes.browsePaper}
/>
</Grid>
</Grid>

View File

@@ -21,11 +21,14 @@ export const OBJECT_BROWSER_REMOVE_ROUTE_LEVEL =
"OBJECT_BROWSER/REMOVE_ROUTE_LEVEL";
export const OBJECT_BROWSER_SET_ALL_ROUTES = "OBJECT_BROWSER/SET_ALL_ROUTES";
export const OBJECT_BROWSER_CREATE_FOLDER = "OBJECT_BROWSER/CREATE_FOLDER";
export const OBJECT_BROWSER_SET_LAST_AS_FILE =
"OBJECT_BROWSER/SET_LAST_AS_FILE";
interface AddRouteAction {
type: typeof OBJECT_BROWSER_ADD_ROUTE;
route: string;
label: string;
routeType: string;
}
interface ResetRoutesList {
@@ -48,23 +51,28 @@ interface CreateFolder {
newRoute: string;
}
interface SetLastAsFile {
type: typeof OBJECT_BROWSER_SET_LAST_AS_FILE;
}
export type ObjectBrowserActionTypes =
| AddRouteAction
| ResetRoutesList
| RemoveRouteLevel
| SetAllRoutes
| CreateFolder;
| CreateFolder
| SetLastAsFile;
export const addRoute = (route: string, label: string) => {
export const addRoute = (route: string, label: string, routeType: string) => {
return {
type: OBJECT_BROWSER_ADD_ROUTE,
route,
label,
routeType,
};
};
export const resetRoutesList = (reset: boolean) => {
console.log("RESET");
return {
type: OBJECT_BROWSER_RESET_ROUTES_LIST,
reset,
@@ -91,3 +99,9 @@ export const createFolder = (newRoute: string) => {
newRoute,
};
};
export const setLastAsFile = () => {
return {
type: OBJECT_BROWSER_SET_LAST_AS_FILE,
};
};

View File

@@ -21,19 +21,23 @@ import {
OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
OBJECT_BROWSER_RESET_ROUTES_LIST,
OBJECT_BROWSER_SET_ALL_ROUTES,
OBJECT_BROWSER_SET_LAST_AS_FILE,
ObjectBrowserActionTypes,
} from "./actions";
export interface Route {
route: string;
label: string;
type: string;
}
export interface ObjectBrowserState {
routesList: Route[];
}
const initialRoute = [{ route: "/object-browser", label: "All Buckets" }];
const initialRoute = [
{ route: "/object-browser", label: "All Buckets", type: "path" },
];
const initialState: ObjectBrowserState = {
routesList: initialRoute,
@@ -47,7 +51,7 @@ export function objectBrowserReducer(
case OBJECT_BROWSER_ADD_ROUTE:
const newRouteList = [
...state.routesList,
{ route: action.route, label: action.label },
{ route: action.route, label: action.label, type: action.routeType },
];
history.push(action.route);
@@ -76,7 +80,12 @@ export function objectBrowserReducer(
splitRoutes.forEach((route) => {
if (route !== "" && route !== "object-browser") {
initRoute = `${initRoute}/${route}`;
routesArray.push({ route: initRoute, label: route });
routesArray.push({
route: initRoute,
label: route,
type: "path",
});
}
});
@@ -97,7 +106,7 @@ export function objectBrowserReducer(
if (folderTrim !== "") {
lastRoute = `${lastRoute}/${folderTrim}`;
const newItem = { route: lastRoute, label: folderTrim };
const newItem = { route: lastRoute, label: folderTrim, type: "path" };
newFoldersRoutes.push(newItem);
}
});
@@ -108,6 +117,20 @@ export function objectBrowserReducer(
...state,
routesList: newFoldersRoutes,
};
case OBJECT_BROWSER_SET_LAST_AS_FILE:
const currentList = state.routesList;
const lastItem = currentList.slice(-1)[0];
if (lastItem.type === "path") {
lastItem.type = "file";
}
const newList = [...currentList.slice(0, -1), lastItem];
return {
...state,
routesList: newList,
};
default:
return state;
}

View File

@@ -257,20 +257,6 @@ const Policies = ({ classes }: IPoliciesProps) => {
records={paginatedRecords}
entityName="Policies"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -25,7 +25,10 @@ import TextField from "@material-ui/core/TextField";
import api from "../../../common/api";
import { policySort } from "../../../utils/sortFunctions";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
import {
actionsTray,
selectorsCommon,
} from "../Common/FormComponents/common/styleLibrary";
import { PolicyList } from "./types";
interface ISelectPolicyProps {
@@ -100,6 +103,7 @@ const styles = (theme: Theme) =>
},
},
...actionsTray,
...selectorsCommon,
});
const PolicySelectors = ({
@@ -188,6 +192,7 @@ const PolicySelectors = ({
records={filteredRecords}
entityName="Policies"
idField="name"
customPaperHeight={classes.multiSelectTable}
radioSelection
/>
</Grid>

View File

@@ -278,20 +278,6 @@ const ServiceAccounts = ({ classes }: IServiceAccountsProps) => {
idField={""}
columns={[{ label: "Service Account", elementKey: "" }]}
itemActions={tableActions}
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 4,
count: records.length,
rowsPerPage: rowsPerPage,
page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -913,8 +913,6 @@ const AddTenant = ({
.invoke("POST", `/api/v1/tenants`, dataSend)
.then((res) => {
const newSrvAcc: NewServiceAccount = {
accessKey: res.access_key,
secretKey: res.secret_key,
console: {
accessKey: res.console.access_key,
secretKey: res.console.secret_key,

View File

@@ -301,20 +301,6 @@ const ListTenants = ({ classes }: ITenantsList) => {
records={filteredRecords}
entityName="Tenants"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -16,7 +16,10 @@
import React, { useState, useEffect } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
import {
containerForHeader,
modalBasic,
} from "../../Common/FormComponents/common/styleLibrary";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button } from "@material-ui/core";
@@ -34,6 +37,7 @@ import api from "../../../../common/api";
import { ITenant, IZone } from "../ListTenants/types";
import Logs from "./Logs/Logs";
import Trace from "./Trace/Trace";
import PageHeader from "../../Common/PageHeader/PageHeader";
interface ITenantDetailsProps {
classes: any;
@@ -90,13 +94,14 @@ const styles = (theme: Theme) =>
textAlign: "right",
},
...modalBasic,
...containerForHeader(theme.spacing(4)),
});
const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
const [selectedTab, setSelectedTab] = useState<number>(0);
const [capacity, setCapacity] = useState<number>(0);
const [zoneCount, setZoneCount] = useState<number>(0);
const [zones, setZones] = useState<IZone[]>([]);
const [serverSets, setServerSets] = useState<IZone[]>([]);
const [instances, setInstances] = useState<number>(0);
const [volumes, setVolumes] = useState<number>(0);
const [addZoneOpen, setAddZone] = useState<boolean>(false);
@@ -157,7 +162,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
setVolumes(totalVolumes);
setInstances(totalInstances);
setZones(resZones);
setServerSets(resZones);
setTenant(res);
setError("");
@@ -193,12 +198,9 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
closeModalAndRefresh={closeReplicationAndRefresh}
/>
)}
<PageHeader label={`Tenant > ${match.params["tenantName"]}`} />
<Grid item xs={12} className={classes.container} />
<Grid container>
<Grid item xs={12}>
<Typography variant="h6">
{`Tenant > ${match.params["tenantName"]}`}
</Typography>
</Grid>
{error !== "" && (
<Grid item xs={12}>
{error}
@@ -214,7 +216,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
<div>{niceBytes(capacity.toString(10))}</div>
<div>Minio:</div>
<div>{tenant ? tenant.image : ""}</div>
<div>Zones:</div>
<div>Clusters:</div>
<div>{zoneCount}</div>
<div>Console:</div>
<div>{tenant ? tenant.console_image : ""}</div>
@@ -238,7 +240,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
}}
aria-label="tenant-tabs"
>
<Tab label="Zones" />
<Tab label="Clusters" />
<Tab label="Logs" />
<Tab label="Trace" />
</Tabs>
@@ -252,7 +254,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
setAddZone(true);
}}
>
Add Zone
Expand Tenant
</Button>
</Grid>
<Grid item xs={12}>
@@ -277,23 +279,9 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
{ label: "# of Drives", elementKey: "volumes" },
]}
isLoading={false}
records={zones}
entityName="Zones"
records={serverSets}
entityName="Servers"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: zoneCount,
rowsPerPage: 10,
page: 0,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
ActionsComponent: MinTablePaginationActions,
onChangePage: () => {},
onChangeRowsPerPage: () => {},
}}
/>
)}
</Grid>

View File

@@ -24,7 +24,6 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { niceBytes, timeFromDate } from "../../../../../common/utils";
import { wsProtocol } from "../../../../../utils/wsUtils";
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
import PageHeader from "../../../Common/PageHeader/PageHeader";
import { Grid } from "@material-ui/core";
import TableWrapper from "../../../Common/TableWrapper/TableWrapper";

View File

@@ -222,7 +222,7 @@ class AddUserContent extends React.Component<
{selectedUser !== null && (
<div className={classes.floatingEnabled}>
<FormSwitchWrapper
indicatorLabel={"Enabled"}
indicatorLabels={["Enabled", "Disabled"]}
checked={enabled}
value={"user_enabled"}
id="user-status"

View File

@@ -27,7 +27,10 @@ import { stringSort } from "../../../utils/sortFunctions";
import { GroupsList } from "../Groups/types";
import get from "lodash/get";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
import {
actionsTray,
selectorsCommon,
} from "../Common/FormComponents/common/styleLibrary";
interface IGroupsProps {
classes: any;
@@ -101,6 +104,7 @@ const styles = (theme: Theme) =>
},
},
...actionsTray,
...selectorsCommon,
});
const GroupsSelectors = ({
@@ -205,6 +209,7 @@ const GroupsSelectors = ({
records={filteredRecords}
entityName="Groups"
idField=""
customPaperHeight={classes.multiSelectTable}
/>
</Grid>
</React.Fragment>

View File

@@ -361,20 +361,6 @@ class Users extends React.Component<IUsersProps, IUsersState> {
records={paginatedRecords}
entityName="Users"
idField="accessKey"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: filteredRecords.length,
rowsPerPage: rowsPerPage,
page: page,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
onChangePage: handleChangePage,
onChangeRowsPerPage: handleChangeRowsPerPage,
ActionsComponent: MinTablePaginationActions,
}}
/>
</Grid>
</Grid>

View File

@@ -38,9 +38,7 @@ import { SystemState } from "../../types";
import { userLoggedIn } from "../../actions";
import api from "../../common/api";
import { ILoginDetails, loginStrategyType } from "./types";
import { setSession } from "../../common/utils";
import history from "../../history";
import { isBoolean } from "util";
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
const styles = (theme: Theme) =>
@@ -226,10 +224,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
.send(loginStrategyPayload[loginStrategy.loginStrategy])
.then((res: any) => {
const bodyResponse = res.body;
if (bodyResponse.sessionId) {
// store the jwt token
setSession(bodyResponse.sessionId);
} else if (bodyResponse.error) {
if (bodyResponse.error) {
setLoginSending(false);
// throw will be moved to catch block once bad login returns 403
throw bodyResponse.error;

View File

@@ -1685,6 +1685,13 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@types/react-copy-to-clipboard@^4.3.0":
version "4.3.0"
resolved "https://registry.yarnpkg.com/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.3.0.tgz#8e07becb4f11cfced4bd36038cb5bdf5c2658be5"
integrity sha512-iideNPRyroENqsOFh1i2Dv3zkviYS9r/9qD9Uh3Z9NNoAAqqa2x53i7iGndGNnJFIo20wIu7Hgh77tx1io8bgw==
dependencies:
"@types/react" "*"
"@types/react-dom@16.9.4":
version "16.9.4"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.4.tgz#0b58df09a60961dcb77f62d4f1832427513420df"
@@ -1726,6 +1733,14 @@
dependencies:
"@types/react" "*"
"@types/react-virtualized@^9.21.10":
version "9.21.10"
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.10.tgz#cd072dc9c889291ace2c4c9de8e8c050da8738b7"
integrity sha512-f5Ti3A7gGdLkPPFNHTrvKblpsPNBiQoSorOEOD+JPx72g/Ng2lOt4MYfhvQFQNgyIrAro+Z643jbcKafsMW2ag==
dependencies:
"@types/prop-types" "*"
"@types/react" "*"
"@types/react@*":
version "16.9.53"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.53.tgz#40cd4f8b8d6b9528aedd1fff8fcffe7a112a3d23"
@@ -3368,6 +3383,13 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
copy-to-clipboard@^3:
version "3.3.1"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
dependencies:
toggle-selection "^1.0.6"
core-js-compat@^3.6.2:
version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c"
@@ -4038,7 +4060,7 @@ dom-helpers@^3.4.0:
dependencies:
"@babel/runtime" "^7.1.2"
dom-helpers@^5.0.1:
dom-helpers@^5.0.1, dom-helpers@^5.1.3:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b"
integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==
@@ -8875,7 +8897,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.4"
prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9076,6 +9098,14 @@ react-codemirror2@^7.1.0:
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-7.2.1.tgz#38dab492fcbe5fb8ebf5630e5bb7922db8d3a10c"
integrity sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==
react-copy-to-clipboard@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz#d82a437e081e68dfca3761fbd57dbf2abdda1316"
integrity sha512-/2t5mLMMPuN5GmdXo6TebFa8IoFxZ+KTDDqYhcDm0PhkgEzSxVvIX26G20s1EB02A4h2UZgwtfymZ3lGJm0OLg==
dependencies:
copy-to-clipboard "^3"
prop-types "^15.5.8"
react-dev-utils@^10.2.1:
version "10.2.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19"
@@ -9290,6 +9320,23 @@ react-transition-group@^4.4.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-virtualized@^9.22.2:
version "9.22.2"
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.2.tgz#217a870bad91e5438f46f01a009e1d8ce1060a5a"
integrity sha512-5j4h4FhxTdOpBKtePSs1yk6LDNT4oGtUwjT7Nkh61Z8vv3fTG/XeOf8J4li1AYaexOwTXnw0HFVxsV0GBUqwRw==
dependencies:
"@babel/runtime" "^7.7.2"
clsx "^1.0.4"
dom-helpers "^5.1.3"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.4"
react-window-infinite-loader@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/react-window-infinite-loader/-/react-window-infinite-loader-1.0.5.tgz#6fe094d538a88978c2c9b623052bc50cb28c2abc"
integrity sha512-IcPIq8lADK3zsAcqoLqQGyduicqR6jWkiK2VUX5sKSI9X/rou6OWlOEexnGyujdNTG7hSG8OVBFEhLSDs4qrxg==
react@^16.13.1:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
@@ -10736,6 +10783,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
toggle-selection@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"

View File

@@ -249,7 +249,13 @@ func getTenantAdminClient(ctx context.Context, client K8sClientI, tenant *operat
log.Println("tenant's secret doesn't contain secretkey")
return nil, errorGeneric
}
mAdmin, pErr := NewAdminClientWithInsecure(svcURL, string(accessKey), string(secretkey), insecure)
// TODO:
// We need to avoid using minio root credentials to talk to tenants, and instead use a different user credentials
// when that its implemented we also need to check here if the tenant has LDAP enabled so we authenticate first against AD
tenantAccessKey := string(accessKey)
tenantSecretKey := string(secretkey)
sessionToken := ""
mAdmin, pErr := NewAdminClientWithInsecure(svcURL, tenantAccessKey, tenantSecretKey, sessionToken, insecure)
if pErr != nil {
return nil, pErr.Cause
}
@@ -681,7 +687,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
return nil, prepareError(errorGeneric)
}
const consoleVersion = "minio/console:v0.4.4"
const consoleVersion = "minio/console:v0.4.6"
minInst.Spec.Console = &operator.ConsoleConfiguration{
Replicas: 1,
Image: consoleVersion,
@@ -778,10 +784,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
return nil, prepareError(err)
}
}
response = &models.CreateTenantResponse{
AccessKey: accessKey,
SecretKey: secretKey,
}
response = &models.CreateTenantResponse{}
// Attach Console Credentials
if enableConsole {
response.Console = &models.CreateTenantResponseConsole{

View File

@@ -1016,7 +1016,7 @@ func Test_UpdateTenantAction(t *testing.T) {
},
params: admin_api.UpdateTenantParams{
Body: &models.UpdateTenantRequest{
ConsoleImage: "minio/console:v0.4.4",
ConsoleImage: "minio/console:v0.4.6",
},
},
},

View File

@@ -19,10 +19,13 @@ package restapi
import (
"context"
"io"
"net/http"
"path/filepath"
"runtime"
"github.com/minio/console/models"
"github.com/minio/console/pkg/auth"
"github.com/minio/console/pkg/auth/ldap"
mcCmd "github.com/minio/mc/cmd"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7/pkg/credentials"
@@ -34,22 +37,22 @@ import (
const globalAppName = "console"
// NewAdminClient gives a new madmin client interface
func NewAdminClient(url, accessKey, secretKey string) (*madmin.AdminClient, *probe.Error) {
return NewAdminClientWithInsecure(url, accessKey, secretKey, false)
func NewAdminClient(url, accessKey, secretKey, sessionToken string) (*madmin.AdminClient, *probe.Error) {
return NewAdminClientWithInsecure(url, accessKey, secretKey, sessionToken, false)
}
// NewAdminClientWithInsecure gives a new madmin client interface either secure or insecure based on parameter
func NewAdminClientWithInsecure(url, accessKey, secretKey string, insecure bool) (*madmin.AdminClient, *probe.Error) {
func NewAdminClientWithInsecure(url, accessKey, secretKey, sessionToken string, insecure bool) (*madmin.AdminClient, *probe.Error) {
appName := filepath.Base(globalAppName)
s3Client, err := s3AdminNew(&mcCmd.Config{
HostURL: url,
AccessKey: accessKey,
SecretKey: secretKey,
AppName: appName,
AppVersion: ConsoleVersion,
AppComments: []string{appName, runtime.GOOS, runtime.GOARCH},
Insecure: insecure,
HostURL: url,
AccessKey: accessKey,
SecretKey: secretKey,
SessionToken: sessionToken,
AppName: appName,
AppVersion: ConsoleVersion,
AppComments: []string{appName, runtime.GOOS, runtime.GOARCH},
Insecure: insecure,
})
if err != nil {
return nil, err.Trace(url)
@@ -304,16 +307,54 @@ func newAdminFromClaims(claims *models.Principal) (*madmin.AdminClient, error) {
if err != nil {
return nil, err
}
stsClient := PrepareSTSClient(false)
adminClient.SetCustomTransport(stsClient.Transport)
adminClient.SetCustomTransport(GetConsoleSTSClient().Transport)
return adminClient, nil
}
var (
consoleAccessKey = getAccessKey()
consoleSecretKey = getSecretKey()
)
// stsClient is a custom http client, this client should not be called directly and instead be
// called using GetConsoleSTSClient() to ensure is initialized and the certificates are loaded correctly
var stsClient *http.Client
// GetConsoleSTSClient will initialize the console STS Client with Custom TLS Transport that with loads certs at .console/certs/CAs
func GetConsoleSTSClient() *http.Client {
if stsClient == nil {
stsClient = PrepareSTSClient(false)
}
return stsClient
}
var consoleLDAPAdminCreds consoleCredentials
func newSuperMAdminClient() (*madmin.AdminClient, error) {
endpoint := getMinIOServer()
accessKeyID := getAccessKey()
secretAccessKey := getSecretKey()
adminClient, pErr := NewAdminClient(endpoint, accessKeyID, secretAccessKey)
accessKey := consoleAccessKey
secretKey := consoleSecretKey
sessionToken := ""
// If LDAP is enabled (External IDP) in minio, then obtain the session tokens associated with the super admin credentials
// configured in console
if ldap.GetLDAPEnabled() {
// initialize LDAP super Admin Credentials once
if consoleLDAPAdminCreds.consoleCredentials == nil {
consoleCredentialsFromLDAP, err := auth.GetCredentialsFromLDAP(GetConsoleSTSClient(), MinioEndpoint, consoleAccessKey, consoleSecretKey)
if err != nil {
return nil, err
}
consoleLDAPAdminCreds = consoleCredentials{consoleCredentials: consoleCredentialsFromLDAP}
}
tokens, err := consoleLDAPAdminCreds.Get()
if err != nil {
return nil, err
}
accessKey = tokens.AccessKeyID
secretKey = tokens.SecretAccessKey
sessionToken = tokens.SessionToken
}
adminClient, pErr := NewAdminClient(MinioEndpoint, accessKey, secretKey, sessionToken)
if pErr != nil {
return nil, pErr.Cause
}

View File

@@ -266,7 +266,6 @@ func (s consoleSTSAssumeRole) IsExpired() bool {
return s.stsAssumeRole.IsExpired()
}
// STSClient contains http.client configuration need it by STSAssumeRole
var (
MinioEndpoint = getMinIOServer()
)
@@ -289,7 +288,7 @@ func newConsoleCredentials(accessKey, secretKey, location string) (*credentials.
if MinioEndpoint == "" {
return nil, errors.New("endpoint cannot be empty for AssumeRoleSTS")
}
creds, err := auth.GetConsoleCredentialsFromLDAP(MinioEndpoint, accessKey, secretKey)
creds, err := auth.GetCredentialsFromLDAP(GetConsoleSTSClient(), MinioEndpoint, accessKey, secretKey)
if err != nil {
return nil, err
}
@@ -307,9 +306,8 @@ func newConsoleCredentials(accessKey, secretKey, location string) (*credentials.
Location: location,
DurationSeconds: xjwt.GetConsoleSTSDurationInSeconds(),
}
stsClient := PrepareSTSClient(false)
stsAssumeRole := &credentials.STSAssumeRole{
Client: stsClient,
Client: GetConsoleSTSClient(),
STSEndpoint: MinioEndpoint,
Options: opts,
}
@@ -329,11 +327,10 @@ func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Cre
// from the provided session token
func newMinioClient(claims *models.Principal) (*minio.Client, error) {
creds := getConsoleCredentialsFromSession(claims)
stsClient := PrepareSTSClient(false)
minioClient, err := minio.New(getMinIOEndpoint(), &minio.Options{
Creds: creds,
Secure: getMinIOEndpointIsSecure(),
Transport: stsClient.Transport,
Transport: GetConsoleSTSClient().Transport,
})
if err != nil {
return nil, err

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"strconv"
"strings"
"time"
"github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/env"
@@ -41,6 +42,8 @@ var TLSPort = "9443"
// TLSRedirect console tls redirect rule
var TLSRedirect = "off"
var SessionDuration = 45 * time.Minute
func getAccessKey() string {
return env.Get(ConsoleAccessKey, "minioadmin")
}

View File

@@ -21,6 +21,7 @@ package restapi
import (
"bytes"
"crypto/tls"
"fmt"
"log"
"net/http"
"strings"
@@ -142,11 +143,13 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
// Add the global public crts as part of global root CAs
for _, publicCrt := range GlobalPublicCerts {
GlobalRootCAs.AddCert(publicCrt)
if GlobalRootCAs != nil {
// Add the global public crts as part of global root CAs
for _, publicCrt := range GlobalPublicCerts {
GlobalRootCAs.AddCert(publicCrt)
}
tlsConfig.RootCAs = GlobalRootCAs
}
tlsConfig.RootCAs = GlobalRootCAs
if GlobalTLSCertsManager != nil {
tlsConfig.GetCertificate = GlobalTLSCertsManager.GetCertificate
}
@@ -168,8 +171,10 @@ func setupMiddlewares(handler http.Handler) http.Handler {
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics
func setupGlobalMiddleware(handler http.Handler) http.Handler {
// handle cookie or authorization header for session
next := AuthenticationMiddleware(handler)
// serve static files
next := FileServerMiddleware(handler)
next = FileServerMiddleware(next)
// Secure middleware, this middleware wrap all the previous handlers and add
// HTTP security headers
secureOptions := secure.Options{
@@ -200,6 +205,31 @@ func setupGlobalMiddleware(handler http.Handler) http.Handler {
return app
}
func AuthenticationMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// prioritize authorization header and skip
if r.Header.Get("Authorization") != "" {
next.ServeHTTP(w, r)
return
}
tokenCookie, err := r.Cookie("token")
if err != nil {
next.ServeHTTP(w, r)
return
}
currentTime := time.Now()
if tokenCookie.Expires.After(currentTime) {
next.ServeHTTP(w, r)
return
}
token := tokenCookie.Value
if token != "" {
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
}
next.ServeHTTP(w, r)
})
}
// FileServerMiddleware serves files from the static folder
func FileServerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

View File

@@ -546,6 +546,12 @@ func init() {
"name": "prefix",
"in": "query",
"required": true
},
{
"type": "string",
"name": "version_id",
"in": "query",
"required": true
}
],
"responses": {
@@ -3279,9 +3285,6 @@ func init() {
"createTenantResponse": {
"type": "object",
"properties": {
"access_key": {
"type": "string"
},
"console": {
"type": "object",
"properties": {
@@ -3292,9 +3295,6 @@ func init() {
"type": "string"
}
}
},
"secret_key": {
"type": "string"
}
}
},
@@ -5412,6 +5412,12 @@ func init() {
"name": "prefix",
"in": "query",
"required": true
},
{
"type": "string",
"name": "version_id",
"in": "query",
"required": true
}
],
"responses": {
@@ -8668,9 +8674,6 @@ func init() {
"createTenantResponse": {
"type": "object",
"properties": {
"access_key": {
"type": "string"
},
"console": {
"type": "object",
"properties": {
@@ -8681,9 +8684,6 @@ func init() {
"type": "string"
}
}
},
"secret_key": {
"type": "string"
}
}
},

View File

@@ -58,6 +58,11 @@ type DownloadObjectParams struct {
In: query
*/
Prefix string
/*
Required: true
In: query
*/
VersionID string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
@@ -81,6 +86,11 @@ func (o *DownloadObjectParams) BindRequest(r *http.Request, route *middleware.Ma
res = append(res, err)
}
qVersionID, qhkVersionID, _ := qs.GetOK("version_id")
if err := o.bindVersionID(qVersionID, qhkVersionID, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -122,3 +132,24 @@ func (o *DownloadObjectParams) bindPrefix(rawData []string, hasKey bool, formats
return nil
}
// bindVersionID binds and validates parameter VersionID from query.
func (o *DownloadObjectParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("version_id", "query", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// AllowEmptyValue: false
if err := validate.RequiredString("version_id", "query", raw); err != nil {
return err
}
o.VersionID = raw
return nil
}

View File

@@ -33,7 +33,8 @@ import (
type DownloadObjectURL struct {
BucketName string
Prefix string
Prefix string
VersionID string
_basePath string
// avoid unkeyed usage
@@ -81,6 +82,11 @@ func (o *DownloadObjectURL) Build() (*url.URL, error) {
qs.Set("prefix", prefixQ)
}
versionIDQ := o.VersionID
if versionIDQ != "" {
qs.Set("version_id", versionIDQ)
}
_result.RawQuery = qs.Encode()
return &_result, nil

View File

@@ -96,7 +96,7 @@ func Test_ResourceQuota(t *testing.T) {
wantErr: false,
},
{
name: "Handle error while fetching storage quota elementss",
name: "Handle error while fetching storage quota elements",
args: args{
ctx: ctx,
client: kClient,

View File

@@ -19,7 +19,9 @@ package restapi
import (
"context"
"log"
"net/http"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
"github.com/minio/console/pkg/acl"
@@ -45,21 +47,36 @@ func registerLoginHandlers(api *operations.ConsoleAPI) {
if err != nil {
return user_api.NewLoginDefault(int(err.Code)).WithPayload(err)
}
return user_api.NewLoginCreated().WithPayload(loginResponse)
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
api.UserAPILoginOauth2AuthHandler = user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
loginResponse, err := getLoginOauth2AuthResponse(params.Body)
if err != nil {
return user_api.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
}
return user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse)
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
api.UserAPILoginOperatorHandler = user_api.LoginOperatorHandlerFunc(func(params user_api.LoginOperatorParams) middleware.Responder {
loginResponse, err := getLoginOperatorResponse(params.Body)
if err != nil {
return user_api.NewLoginOperatorDefault(int(err.Code)).WithPayload(err)
}
return user_api.NewLoginOperatorCreated().WithPayload(loginResponse)
// Custom response writer to set the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
cookie := NewSessionCookieForConsole(loginResponse.SessionID)
http.SetCookie(w, &cookie)
user_api.NewLoginOperatorCreated().WithPayload(loginResponse).WriteResponse(w, p)
})
})
}

View File

@@ -17,6 +17,9 @@
package restapi
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/console/models"
"github.com/minio/console/restapi/operations"
@@ -27,7 +30,14 @@ func registerLogoutHandlers(api *operations.ConsoleAPI) {
// logout from console
api.UserAPILogoutHandler = user_api.LogoutHandlerFunc(func(params user_api.LogoutParams, session *models.Principal) middleware.Responder {
getLogoutResponse(session)
return user_api.NewLogoutOK()
// Custom response writer to expire the session cookies
return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) {
expiredCookie := ExpireSessionCookie()
// this will tell the browser to clear the cookie and invalidate user session
// additionally we are deleting the cookie from the client side
http.SetCookie(w, &expiredCookie)
user_api.NewLogoutOK().WriteResponse(w, p)
})
})
}

View File

@@ -221,18 +221,17 @@ func getDownloadObjectResponse(session *models.Principal, params user_api.Downlo
// create a mc S3Client interface implementation
// defining the client to be used
mcClient := mcClient{client: s3Client}
object, err := downloadObject(ctx, mcClient)
object, err := downloadObject(ctx, mcClient, params.VersionID)
if err != nil {
return nil, prepareError(err)
}
return object, nil
}
func downloadObject(ctx context.Context, client MCClient) (io.ReadCloser, error) {
// TODO: handle version
func downloadObject(ctx context.Context, client MCClient, versionID string) (io.ReadCloser, error) {
// TODO: handle encripted files
var reader io.ReadCloser
reader, pErr := client.get(ctx, mc.GetOptions{})
reader, pErr := client.get(ctx, mc.GetOptions{VersionID: versionID})
if pErr != nil {
return nil, pErr.Cause
}
@@ -285,7 +284,7 @@ func deleteMultipleObjects(ctx context.Context, client MCClient, recursive bool)
isRemoveBucket := false
isIncomplete := false
isBypass := false
listOpts := mc.ListOptions{IsRecursive: recursive, IsIncomplete: isIncomplete, ShowDir: mc.DirNone}
listOpts := mc.ListOptions{Recursive: recursive, Incomplete: isIncomplete, ShowDir: mc.DirNone}
// TODO: support older Versions
contentCh := make(chan *mc.ClientContent, 1)

View File

@@ -19,8 +19,10 @@ package restapi
import (
"crypto/rand"
"io"
"net/http"
"os"
"strings"
"time"
)
// Do not use:
@@ -102,3 +104,37 @@ func FileExists(filename string) bool {
}
return !info.IsDir()
}
func NewSessionCookieForConsole(token string) http.Cookie {
expiration := time.Now().Add(SessionDuration)
return http.Cookie{
Path: "/",
Name: "token",
Value: token,
MaxAge: int(SessionDuration.Seconds()), // 45 minutes
Expires: expiration,
HttpOnly: true,
// if len(GlobalPublicCerts) > 0 is true, that means Console is running with TLS enable and the browser
// should not leak any cookie if we access the site using HTTP
Secure: len(GlobalPublicCerts) > 0,
// read more: https://web.dev/samesite-cookies-explained/
SameSite: http.SameSiteLaxMode,
}
}
func ExpireSessionCookie() http.Cookie {
return http.Cookie{
Path: "/",
Name: "token",
Value: "",
MaxAge: -1,
Expires: time.Now().Add(-100 * time.Hour),
HttpOnly: true,
// if len(GlobalPublicCerts) > 0 is true, that means Console is running with TLS enable and the browser
// should not leak any cookie if we access the site using HTTP
Secure: len(GlobalPublicCerts) > 0,
// read more: https://web.dev/samesite-cookies-explained/
SameSite: http.SameSiteLaxMode,
}
}

View File

@@ -195,7 +195,7 @@ func newWebSocketTenantAdminClient(conn *websocket.Conn, session *models.Princip
minTenant.EnsureDefaults()
svcURL := GetTenantServiceURL(minTenant)
// TODO: in the feature we need to load all tenants public certificates under ~/.console/certs/CAs to avoid using insecure: true
mAdmin, err := getTenantAdminClient(
ctx,
k8sClient,

View File

@@ -328,6 +328,10 @@ paths:
in: query
required: true
type: string
- name: version_id
in: query
required: true
type: string
responses:
200:
description: A successful response.
@@ -2967,10 +2971,6 @@ definitions:
createTenantResponse:
type: object
properties:
access_key:
type: string
secret_key:
type: string
console:
type: object
properties: