Merge pull request #60 from roman-khimov/drop-cdn-sdk

Drop old sdk
This commit is contained in:
Angira Kekteeva 2021-05-27 21:42:59 +03:00 committed by GitHub
commit 1e6d3ebea6
24 changed files with 1134 additions and 170 deletions

View file

@ -12,12 +12,12 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4" v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
sdk "github.com/nspcc-dev/cdn-sdk"
"github.com/nspcc-dev/cdn-sdk/creds/bearer"
"github.com/nspcc-dev/cdn-sdk/creds/hcs"
"github.com/nspcc-dev/cdn-sdk/creds/s3"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/token" "github.com/nspcc-dev/neofs-api-go/pkg/token"
sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/neofs-s3-gw/authmate"
"github.com/nspcc-dev/neofs-s3-gw/creds/bearer"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -36,7 +36,7 @@ type (
// Params stores node connection parameters. // Params stores node connection parameters.
Params struct { Params struct {
Client sdk.Client Client sdk.ClientPlant
Logger *zap.Logger Logger *zap.Logger
Credential hcs.Credentials Credential hcs.Credentials
} }
@ -55,7 +55,7 @@ func (p prs) Seek(_ int64, _ int) (int64, error) {
var _ io.ReadSeeker = prs(0) var _ io.ReadSeeker = prs(0)
// New creates an instance of AuthCenter. // New creates an instance of AuthCenter.
func New(obj sdk.ObjectClient, key hcs.PrivateKey) Center { func New(obj sdk.ClientPlant, key hcs.PrivateKey) Center {
return &center{ return &center{
cli: bearer.New(obj, key), cli: bearer.New(obj, key),
reg: &regexpSubmatcher{re: authorizationFieldRegexp}, reg: &regexpSubmatcher{re: authorizationFieldRegexp},
@ -100,7 +100,7 @@ func (c *center) Authenticate(r *http.Request) (*token.BearerToken, error) {
return nil, err return nil, err
} }
secret, err := s3.SecretAccessKey(tkn) secret, err := authmate.BearerToAccessKey(tkn)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -42,7 +42,15 @@ func (n *layer) containerInfo(ctx context.Context, cid *container.ID) (*BucketIn
} }
) )
if res, err = n.cli.Container().Get(ctx, cid); err != nil { conn, _, err := n.cli.ConnectionArtifacts()
if err != nil {
n.log.Error("failed to get connection from the pool",
zap.String("request_id", rid),
zap.Error(err))
return nil, err
}
res, err = conn.GetContainer(ctx, cid)
if err != nil {
n.log.Error("could not fetch container", n.log.Error("could not fetch container",
zap.Stringer("cid", cid), zap.Stringer("cid", cid),
zap.String("request_id", rid), zap.String("request_id", rid),
@ -84,7 +92,15 @@ func (n *layer) containerList(ctx context.Context) ([]*BucketInfo, error) {
rid = api.GetRequestID(ctx) rid = api.GetRequestID(ctx)
) )
if res, err = n.cli.Container().List(ctx, own); err != nil { conn, _, err := n.cli.ConnectionArtifacts()
if err != nil {
n.log.Error("failed to get connection from the pool",
zap.String("request_id", rid),
zap.Error(err))
return nil, err
}
res, err = conn.ListContainers(ctx, own)
if err != nil {
n.log.Error("could not fetch container", n.log.Error("could not fetch container",
zap.String("request_id", rid), zap.String("request_id", rid),
zap.Error(err)) zap.Error(err))

View file

@ -8,13 +8,15 @@ import (
"net/url" "net/url"
"time" "time"
sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-api-go/pkg/client"
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
"github.com/nspcc-dev/cdn-sdk/pool"
"github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
"github.com/nspcc-dev/neofs-http-gw/connections"
sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/creds/neofs"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -22,13 +24,13 @@ import (
type ( type (
layer struct { layer struct {
cli sdk.Client cli sdk.ClientPlant
log *zap.Logger log *zap.Logger
} }
// Params stores basic API parameters. // Params stores basic API parameters.
Params struct { Params struct {
Pool pool.Client Pool connections.Pool
Logger *zap.Logger Logger *zap.Logger
Timeout time.Duration Timeout time.Duration
Credential neofs.Credentials Credential neofs.Credentials
@ -96,7 +98,7 @@ var (
// NewLayer creates instance of layer. It checks credentials // NewLayer creates instance of layer. It checks credentials
// and establishes gRPC connection with node. // and establishes gRPC connection with node.
func NewLayer(log *zap.Logger, cli sdk.Client) Client { func NewLayer(log *zap.Logger, cli sdk.ClientPlant) Client {
return &layer{ return &layer{
cli: cli, cli: cli,
log: log, log: log,
@ -105,16 +107,21 @@ func NewLayer(log *zap.Logger, cli sdk.Client) Client {
// Owner returns owner id from BearerToken (context) or from client owner. // Owner returns owner id from BearerToken (context) or from client owner.
func (n *layer) Owner(ctx context.Context) *owner.ID { func (n *layer) Owner(ctx context.Context) *owner.ID {
if tkn, err := sdk.BearerToken(ctx); err != nil && tkn != nil { if tkn, ok := ctx.Value(api.BearerTokenKey).(*token.BearerToken); ok && tkn != nil {
return tkn.Issuer() return tkn.Issuer()
} }
return n.cli.Owner() return n.cli.OwnerID()
} }
// Get NeoFS Object by refs.Address (should be used by auth.Center). // Get NeoFS Object by refs.Address (should be used by auth.Center).
func (n *layer) Get(ctx context.Context, address *object.Address) (*object.Object, error) { func (n *layer) Get(ctx context.Context, address *object.Address) (*object.Object, error) {
return n.cli.Object().Get(ctx, address) conn, tok, err := n.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
ops := new(client.GetObjectParams).WithAddress(address)
return conn.GetObject(ctx, ops, client.WithSession(tok))
} }
// GetBucketInfo returns bucket info by name. // GetBucketInfo returns bucket info by name.

View file

@ -8,7 +8,7 @@ import (
"strconv" "strconv"
"time" "time"
sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-api-go/pkg/client"
"github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
@ -33,16 +33,20 @@ type (
// objectSearch returns all available objects by search params. // objectSearch returns all available objects by search params.
func (n *layer) objectSearch(ctx context.Context, p *findParams) ([]*object.ID, error) { func (n *layer) objectSearch(ctx context.Context, p *findParams) ([]*object.ID, error) {
opts := []sdk.ObjectSearchOption{ var opts object.SearchFilters
sdk.SearchRootObjects(),
} opts.AddRootFilter()
if filename, err := url.QueryUnescape(p.val); err != nil { if filename, err := url.QueryUnescape(p.val); err != nil {
return nil, err return nil, err
} else if filename != "" { } else if filename != "" {
opts = append(opts, sdk.SearchByFilename(filename)) opts.AddFilter(object.AttributeFileName, filename, object.MatchStringEqual)
} }
return n.cli.Object().Search(ctx, p.cid, opts...) conn, _, err := n.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
return conn.SearchObject(ctx, new(client.SearchObjectParams).WithContainerID(p.cid).WithSearchFilters(opts))
} }
// objectFindID returns object id (uuid) based on it's nice name in s3. If // objectFindID returns object id (uuid) based on it's nice name in s3. If
@ -61,14 +65,24 @@ func (n *layer) objectFindID(ctx context.Context, p *findParams) (*object.ID, er
// objectHead returns all object's headers. // objectHead returns all object's headers.
func (n *layer) objectHead(ctx context.Context, address *object.Address) (*object.Object, error) { func (n *layer) objectHead(ctx context.Context, address *object.Address) (*object.Object, error) {
return n.cli.Object().Head(ctx, address, sdk.WithFullHeaders()) conn, _, err := n.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
ops := new(client.ObjectHeaderParams).WithAddress(address).WithAllFields()
return conn.GetObjectHeader(ctx, ops)
} }
// objectGet and write it into provided io.Reader. // objectGet and write it into provided io.Reader.
func (n *layer) objectGet(ctx context.Context, p *getParams) (*object.Object, error) { func (n *layer) objectGet(ctx context.Context, p *getParams) (*object.Object, error) {
conn, tok, err := n.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
// prepare length/offset writer // prepare length/offset writer
w := newWriter(p.Writer, p.offset, p.length) w := newWriter(p.Writer, p.offset, p.length)
return n.cli.Object().Get(ctx, p.address, sdk.WithGetWriter(w)) ops := new(client.GetObjectParams).WithAddress(p.address).WithPayloadWriter(w)
return conn.GetObject(ctx, ops, client.WithSession(tok))
} }
// objectPut into NeoFS, took payload from io.Reader. // objectPut into NeoFS, took payload from io.Reader.
@ -78,8 +92,6 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo,
obj string obj string
bkt *BucketInfo bkt *BucketInfo
own = n.Owner(ctx) own = n.Owner(ctx)
address *object.Address
) )
if obj, err = url.QueryUnescape(p.Object); err != nil { if obj, err = url.QueryUnescape(p.Object); err != nil {
@ -123,12 +135,23 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo,
raw.SetAttributes(attributes...) raw.SetAttributes(attributes...)
r := newDetector(p.Reader) r := newDetector(p.Reader)
if address, err = n.cli.Object().Put(ctx, raw.Object(), sdk.WithPutReader(r)); err != nil { conn, tok, err := n.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
ops := new(client.PutObjectParams).WithObject(raw.Object()).WithPayloadReader(r)
oid, err := conn.PutObject(
ctx,
ops,
client.WithSession(tok),
)
if err != nil {
return nil, err return nil, err
} }
return &ObjectInfo{ return &ObjectInfo{
id: address.ObjectID(), id: oid,
Owner: own, Owner: own,
Bucket: p.Bucket, Bucket: p.Bucket,
@ -142,5 +165,11 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo,
// objectDelete puts tombstone object into neofs. // objectDelete puts tombstone object into neofs.
func (n *layer) objectDelete(ctx context.Context, address *object.Address) error { func (n *layer) objectDelete(ctx context.Context, address *object.Address) error {
return n.cli.Object().Delete(ctx, address) conn, _, err := n.cli.ConnectionArtifacts()
if err != nil {
return err
}
dop := new(client.DeleteObjectParams)
dop.WithAddress(address)
return conn.DeleteObject(ctx, dop)
} }

View file

@ -1,14 +1,20 @@
package api package api
import ( import (
"context"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
sdk "github.com/nspcc-dev/cdn-sdk"
"github.com/nspcc-dev/neofs-s3-gw/api/auth" "github.com/nspcc-dev/neofs-s3-gw/api/auth"
"go.uber.org/zap" "go.uber.org/zap"
) )
// KeyWrapper is wrapper for context keys.
type KeyWrapper string
// BearerTokenKey is an ID used to store bearer token in a context.
var BearerTokenKey = KeyWrapper("__context_bearer_token_key")
// AttachUserAuth adds user authentication via center to router using log for logging. // AttachUserAuth adds user authentication via center to router using log for logging.
func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) { func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) {
router.Use(func(h http.Handler) http.Handler { router.Use(func(h http.Handler) http.Handler {
@ -21,7 +27,7 @@ func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) {
} }
h.ServeHTTP(w, r.WithContext( h.ServeHTTP(w, r.WithContext(
sdk.SetBearerToken(r.Context(), token))) context.WithValue(r.Context(), BearerTokenKey, token)))
}) })
}) })
} }

View file

@ -3,6 +3,8 @@ package authmate
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -10,31 +12,34 @@ import (
"strconv" "strconv"
"time" "time"
sdk "github.com/nspcc-dev/cdn-sdk"
"github.com/nspcc-dev/cdn-sdk/creds/bearer"
"github.com/nspcc-dev/cdn-sdk/creds/hcs"
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
"github.com/nspcc-dev/cdn-sdk/creds/s3"
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl" "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/netmap" "github.com/nspcc-dev/neofs-api-go/pkg/netmap"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-api-go/pkg/token" "github.com/nspcc-dev/neofs-api-go/pkg/token"
sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/neofs-node/pkg/policy" "github.com/nspcc-dev/neofs-node/pkg/policy"
"github.com/nspcc-dev/neofs-s3-gw/creds/bearer"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/nspcc-dev/neofs-s3-gw/creds/neofs"
"go.uber.org/zap" "go.uber.org/zap"
) )
const defaultAuthContainerBasicACL uint32 = 0b00111100100011001000110011001100 const (
defaultAuthContainerBasicACL uint32 = 0b00111100100011001000110011001100
containerCreationTimeout = 120 * time.Second
containerPollInterval = 5 * time.Second
)
// Agent contains client communicating with NeoFS and logger. // Agent contains client communicating with NeoFS and logger.
type Agent struct { type Agent struct {
cli sdk.Client cli sdk.ClientPlant
log *zap.Logger log *zap.Logger
} }
// New creates an object of type Agent that consists of Client and logger. // New creates an object of type Agent that consists of Client and logger.
func New(log *zap.Logger, client sdk.Client) *Agent { func New(log *zap.Logger, client sdk.ClientPlant) *Agent {
return &Agent{log: log, cli: client} return &Agent{log: log, cli: client}
} }
@ -70,9 +75,14 @@ type (
) )
func (a *Agent) checkContainer(ctx context.Context, cid *container.ID, friendlyName string) (*container.ID, error) { func (a *Agent) checkContainer(ctx context.Context, cid *container.ID, friendlyName string) (*container.ID, error) {
conn, _, err := a.cli.ConnectionArtifacts()
if err != nil {
return nil, err
}
if cid != nil { if cid != nil {
// check that container exists // check that container exists
_, err := a.cli.Container().Get(ctx, cid) _, err = conn.GetContainer(ctx, cid)
return cid, err return cid, err
} }
@ -87,9 +97,31 @@ func (a *Agent) checkContainer(ctx context.Context, cid *container.ID, friendlyN
container.WithAttribute(container.AttributeName, friendlyName), container.WithAttribute(container.AttributeName, friendlyName),
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10))) container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
return a.cli.Container().Put(ctx, cnr, cid, err = conn.PutContainer(ctx, cnr)
sdk.ContainerPutAndWait(), if err != nil {
sdk.ContainerPutWithTimeout(120*time.Second)) return nil, err
}
wctx, cancel := context.WithTimeout(ctx, containerCreationTimeout)
defer cancel()
ticker := time.NewTimer(containerPollInterval)
defer ticker.Stop()
wdone := wctx.Done()
done := ctx.Done()
for {
select {
case <-done:
return nil, ctx.Err()
case <-wdone:
return nil, wctx.Err()
case <-ticker.C:
_, err = conn.GetContainer(ctx, cid)
if err == nil {
return cid, nil
}
ticker.Reset(containerPollInterval)
}
}
} }
// IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key. // IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key.
@ -121,13 +153,13 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
zap.Stringer("owner_tkn", tkn.Issuer())) zap.Stringer("owner_tkn", tkn.Issuer()))
address, err := bearer. address, err := bearer.
New(a.cli.Object(), options.OwnerPrivateKey). New(a.cli, options.OwnerPrivateKey).
Put(ctx, cid, tkn, options.GatesPublicKeys...) Put(ctx, cid, tkn, options.GatesPublicKeys...)
if err != nil { if err != nil {
return fmt.Errorf("failed to put bearer token: %w", err) return fmt.Errorf("failed to put bearer token: %w", err)
} }
secret, err := s3.SecretAccessKey(tkn) secret, err := BearerToAccessKey(tkn)
if err != nil { if err != nil {
return fmt.Errorf("failed to get bearer token secret key: %w", err) return fmt.Errorf("failed to get bearer token secret key: %w", err)
} }
@ -146,7 +178,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
// ObtainSecret receives an existing secret access key from NeoFS and // ObtainSecret receives an existing secret access key from NeoFS and
// writes to io.Writer the secret access key. // writes to io.Writer the secret access key.
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error { func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
bearerCreds := bearer.New(a.cli.Object(), options.GatePrivateKey) bearerCreds := bearer.New(a.cli, options.GatePrivateKey)
address := object.NewAddress() address := object.NewAddress()
if err := address.Parse(options.SecretAddress); err != nil { if err := address.Parse(options.SecretAddress); err != nil {
return fmt.Errorf("failed to parse secret address: %w", err) return fmt.Errorf("failed to parse secret address: %w", err)
@ -157,7 +189,7 @@ func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSe
return fmt.Errorf("failed to get bearer token: %w", err) return fmt.Errorf("failed to get bearer token: %w", err)
} }
secret, err := s3.SecretAccessKey(tkn) secret, err := BearerToAccessKey(tkn)
if err != nil { if err != nil {
return fmt.Errorf("failed to get bearer token secret key: %w", err) return fmt.Errorf("failed to get bearer token secret key: %w", err)
} }
@ -234,3 +266,14 @@ func buildBearerToken(key *ecdsa.PrivateKey, oid *owner.ID, table *eacl.Table) (
return bearerToken, bearerToken.SignToken(key) return bearerToken, bearerToken.SignToken(key)
} }
// BearerToAccessKey returns secret access key generated from given BearerToken.
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
data, err := tkn.Marshal()
if err != nil {
return "", err
}
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:]), nil
}

View file

@ -6,15 +6,16 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"os/signal"
"syscall"
"time" "time"
sdk "github.com/nspcc-dev/cdn-sdk"
"github.com/nspcc-dev/cdn-sdk/creds/hcs"
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
"github.com/nspcc-dev/cdn-sdk/grace"
"github.com/nspcc-dev/cdn-sdk/pool"
"github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-http-gw/connections"
sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/neofs-s3-gw/authmate" "github.com/nspcc-dev/neofs-s3-gw/authmate"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/nspcc-dev/neofs-s3-gw/creds/neofs"
"github.com/nspcc-dev/neofs-s3-gw/internal/version" "github.com/nspcc-dev/neofs-s3-gw/internal/version"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.uber.org/zap" "go.uber.org/zap"
@ -66,10 +67,11 @@ func prepare() (context.Context, *zap.Logger) {
var ( var (
err error err error
log = zap.NewNop() log = zap.NewNop()
ctx, _ = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
) )
if !logEnabledFlag { if !logEnabledFlag {
return grace.Context(log), log return ctx, log
} else if logDebugEnabledFlag { } else if logDebugEnabledFlag {
zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel) zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
} }
@ -78,7 +80,7 @@ func prepare() (context.Context, *zap.Logger) {
panic(err) panic(err)
} }
return grace.Context(log), log return ctx, log
} }
func main() { func main() {
@ -363,25 +365,23 @@ func fetchHCSCredentials(val string) (hcs.Credentials, error) {
return hcs.NewCredentials(val) return hcs.NewCredentials(val)
} }
func createSDKClient(ctx context.Context, log *zap.Logger, neofsCreds neofs.Credentials, peerAddress string) (sdk.Client, error) { func createSDKClient(ctx context.Context, log *zap.Logger, neofsCreds neofs.Credentials, peerAddress string) (sdk.ClientPlant, error) {
log.Debug("prepare connection pool") log.Debug("prepare connection pool")
p, err := pool.New(ctx, pb := new(connections.PoolBuilder)
pool.WithLogger(log), pb.AddNode(peerAddress, 1)
pool.WithAddress(peerAddress),
pool.WithCredentials(neofsCreds), opts := &connections.PoolBuilderOptions{
pool.WithAPIPreparer(sdk.APIPreparer), Key: neofsCreds.PrivateKey(),
pool.WithConnectTimeout(poolConnectTimeout), NodeConnectionTimeout: poolConnectTimeout,
pool.WithRequestTimeout(poolRequestTimeout)) NodeRequestTimeout: poolRequestTimeout,
}
pool, err := pb.Build(ctx, opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create connection pool: %w", err) return nil, fmt.Errorf("failed to create connection pool: %w", err)
} }
log.Debug("prepare sdk client") log.Debug("prepare sdk client")
return sdk.New(ctx, return sdk.NewClientPlant(ctx, pool, neofsCreds)
sdk.WithLogger(log),
sdk.WithCredentials(neofsCreds),
sdk.WithConnectionPool(p),
sdk.WithAPIPreparer(sdk.APIPreparer))
} }

View file

@ -18,6 +18,7 @@ const (
defaultContentType = "text/plain; charset=utf-8" defaultContentType = "text/plain; charset=utf-8"
) )
//nolint:deadcode,unused // TODO
func attachHealthy(r *mux.Router, h Healthy) { func attachHealthy(r *mux.Router, h Healthy) {
healthy := r.PathPrefix(systemPath + "/-"). healthy := r.PathPrefix(systemPath + "/-").
Subrouter(). Subrouter().

View file

@ -9,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/nspcc-dev/neofs-http-gw/connections"
"github.com/nspcc-dev/neofs-s3-gw/internal/version" "github.com/nspcc-dev/neofs-s3-gw/internal/version"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -110,8 +111,8 @@ var ignore = map[string]struct{}{
func (empty) Read([]byte) (int, error) { return 0, io.EOF } func (empty) Read([]byte) (int, error) { return 0, io.EOF }
func fetchPeers(l *zap.Logger, v *viper.Viper) map[string]float64 { func fetchPeers(l *zap.Logger, v *viper.Viper) *connections.PoolBuilder {
peers := make(map[string]float64) pb := new(connections.PoolBuilder)
for i := 0; ; i++ { for i := 0; ; i++ {
key := cfgPeers + "." + strconv.Itoa(i) + "." key := cfgPeers + "." + strconv.Itoa(i) + "."
@ -122,14 +123,17 @@ func fetchPeers(l *zap.Logger, v *viper.Viper) map[string]float64 {
l.Warn("skip, empty address") l.Warn("skip, empty address")
break break
} }
if weight <= 0 { // unspecified or wrong
weight = 1
}
pb.AddNode(address, weight)
peers[address] = weight l.Info("added connection peer",
l.Info("add connection peer",
zap.String("address", address), zap.String("address", address),
zap.Float64("weight", weight)) zap.Float64("weight", weight))
} }
return peers return pb
} }
func fetchDomains(v *viper.Viper) []string { func fetchDomains(v *viper.Viper) []string {

View file

@ -2,29 +2,26 @@ package main
import ( import (
"context" "context"
"errors" "math"
"net" "net"
"net/http" "net/http"
"os"
sdk "github.com/nspcc-dev/cdn-sdk" "github.com/nspcc-dev/neofs-http-gw/connections"
"github.com/nspcc-dev/cdn-sdk/creds/hcs" sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/cdn-sdk/creds/neofs"
"github.com/nspcc-dev/cdn-sdk/pool"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/auth" "github.com/nspcc-dev/neofs-s3-gw/api/auth"
"github.com/nspcc-dev/neofs-s3-gw/api/handler" "github.com/nspcc-dev/neofs-s3-gw/api/handler"
"github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/nspcc-dev/neofs-s3-gw/creds/neofs"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
) )
type ( type (
// App is the main application structure. // App is the main application structure.
App struct { App struct {
cli pool.Client cli sdk.ClientPlant
ctr auth.Center ctr auth.Center
log *zap.Logger log *zap.Logger
cfg *viper.Viper cfg *viper.Viper
@ -46,10 +43,10 @@ type (
func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App { func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
var ( var (
pool connections.Pool
err error err error
tls *tlsConfig tls *tlsConfig
cli sdk.Client cli sdk.ClientPlant
con pool.Client
caller api.Handler caller api.Handler
ctr auth.Center ctr auth.Center
obj layer.Client obj layer.Client
@ -57,7 +54,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
hcsCred hcs.Credentials hcsCred hcs.Credentials
nfsCred neofs.Credentials nfsCred neofs.Credentials
peers = fetchPeers(l, v) poolPeers = fetchPeers(l, v)
reBalance = defaultRebalanceTimer reBalance = defaultRebalanceTimer
conTimeout = defaultConnectTimeout conTimeout = defaultConnectTimeout
@ -109,56 +106,30 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
zap.String("HCS", hcsCredential), zap.String("HCS", hcsCredential),
zap.String("NeoFS", nfsCredential)) zap.String("NeoFS", nfsCredential))
poolOptions := []pool.Option{ opts := &connections.PoolBuilderOptions{
pool.WithLogger(l), Key: nfsCred.PrivateKey(),
pool.WithWeightPool(peers), NodeConnectionTimeout: conTimeout,
pool.WithCredentials(nfsCred), NodeRequestTimeout: reqTimeout,
pool.WithTickerTimeout(reBalance), ClientRebalanceInterval: reBalance,
pool.WithConnectTimeout(conTimeout), SessionExpirationEpoch: math.MaxUint64,
pool.WithRequestTimeout(reqTimeout), KeepaliveTime: v.GetDuration(cfgKeepaliveTime),
pool.WithAPIPreparer(sdk.APIPreparer), KeepaliveTimeout: v.GetDuration(cfgKeepaliveTimeout),
pool.WithGRPCOptions( KeepalivePermitWoStream: v.GetBool(cfgKeepalivePermitWithoutStream),
grpc.WithBlock(),
grpc.WithInsecure(),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: v.GetDuration(cfgKeepaliveTime),
Timeout: v.GetDuration(cfgKeepaliveTimeout),
PermitWithoutStream: v.GetBool(cfgKeepalivePermitWithoutStream),
}))}
if con, err = pool.New(ctx, poolOptions...); err != nil {
l.Fatal("could not prepare pool connections", zap.Error(err))
} }
pool, err = poolPeers.Build(ctx, opts)
{ // should establish connection with NeoFS Storage Nodes if err != nil {
ctx, cancel := context.WithTimeout(ctx, conTimeout) l.Fatal("failed to create connection pool", zap.Error(err))
defer cancel()
if _, err = con.Connection(ctx); err != nil {
if errors.Is(err, context.Canceled) {
l.Info("connection canceled")
os.Exit(0)
} }
cli, err = sdk.NewClientPlant(ctx, pool, nfsCred)
l.Fatal("could not establish connection", if err != nil {
zap.Error(err)) l.Fatal("failed to create neofs client plant")
}
}
if cli, err = sdk.New(ctx,
sdk.WithLogger(l),
sdk.WithConnectionPool(con),
sdk.WithCredentials(nfsCred),
sdk.WithAPIPreparer(sdk.APIPreparer)); err != nil {
l.Fatal("could not prepare sdk client",
zap.Error(err))
} }
// prepare object layer // prepare object layer
obj = layer.NewLayer(l, cli) obj = layer.NewLayer(l, cli)
// prepare auth center // prepare auth center
ctr = auth.New(cli.Object(), hcsCred.PrivateKey()) ctr = auth.New(cli, hcsCred.PrivateKey())
if caller, err = handler.New(l, obj); err != nil { if caller, err = handler.New(l, obj); err != nil {
l.Fatal("could not initialize API handler", zap.Error(err)) l.Fatal("could not initialize API handler", zap.Error(err))
@ -166,7 +137,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
return &App{ return &App{
ctr: ctr, ctr: ctr,
cli: con, cli: cli,
log: l, log: l,
cfg: v, cfg: v,
obj: obj, obj: obj,
@ -184,12 +155,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
func (a *App) Wait() { func (a *App) Wait() {
a.log.Info("application started") a.log.Info("application started")
select { <-a.webDone // wait for web-server to be stopped
case <-a.wrkDone: // wait for worker is stopped
<-a.webDone
case <-a.webDone: // wait for web-server is stopped
<-a.wrkDone
}
a.log.Info("application finished") a.log.Info("application finished")
} }
@ -212,7 +178,7 @@ func (a *App) Server(ctx context.Context) {
router := newS3Router() router := newS3Router()
// Attach app-specific routes: // Attach app-specific routes:
attachHealthy(router, a.cli) // attachHealthy(router, a.cli)
attachMetrics(router, a.cfg, a.log) attachMetrics(router, a.cfg, a.log)
attachProfiler(router, a.cfg, a.log) attachProfiler(router, a.cfg, a.log)
@ -258,10 +224,3 @@ func (a *App) Server(ctx context.Context) {
close(a.webDone) close(a.webDone)
} }
// Worker runs client worker.
func (a *App) Worker(ctx context.Context) {
a.cli.Worker(ctx)
a.log.Info("stopping worker")
close(a.wrkDone)
}

View file

@ -1,8 +1,11 @@
package main package main
import ( import (
"github.com/nspcc-dev/cdn-sdk/grace" "context"
"github.com/nspcc-dev/cdn-sdk/logger" "os/signal"
"syscall"
"github.com/nspcc-dev/neofs-http-gw/logger"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -41,12 +44,11 @@ func main() {
var ( var (
v = newSettings() v = newSettings()
l = newLogger(v) l = newLogger(v)
g = grace.Context(l) g, _ = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
a = newApp(g, l, v) a = newApp(g, l, v)
) )
go a.Server(g) go a.Server(g)
go a.Worker(g)
a.Wait() a.Wait()
} }

View file

@ -0,0 +1,29 @@
package accessbox
import "github.com/nspcc-dev/neofs-api-go/pkg/token"
type (
// Box provides marshalling/unmarshalling for the token.
Box interface {
Marshal() ([]byte, error)
Unmarshal([]byte) error
}
// Encoder provides encoding method.
Encoder interface {
Encode(Box) error
}
// Decoder provides decoding method.
Decoder interface {
Decode(Box) error
}
// BearerTokenBox is a marshalling/unmarshalling bearer token wrapper.
BearerTokenBox interface {
Box
Token() *token.BearerToken
SetToken(*token.BearerToken)
}
)

View file

@ -0,0 +1,43 @@
package accessbox
import (
"github.com/nspcc-dev/neofs-api-go/pkg/token"
)
type bearerBox struct {
tkn *token.BearerToken
}
// NewBearerBox wraps given bearer token into BearerTokenBox.
func NewBearerBox(token *token.BearerToken) BearerTokenBox {
return &bearerBox{tkn: token}
}
// Marshal serializes bearer token.
func (b *bearerBox) Marshal() ([]byte, error) {
return b.tkn.Marshal(nil)
}
// Marshal initializes bearer box from its serialized representation.
func (b *bearerBox) Unmarshal(data []byte) error {
tkn := token.NewBearerToken()
err := tkn.Unmarshal(data)
if err != nil {
return err
}
b.SetToken(tkn)
return nil
}
// Token unwraps bearer token from the box.
func (b *bearerBox) Token() *token.BearerToken {
return b.tkn
}
// SetToken sets new token in the box.
func (b *bearerBox) SetToken(tkn *token.BearerToken) {
b.tkn = tkn
}

View file

@ -0,0 +1,161 @@
package accessbox
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/binary"
"strconv"
"testing"
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/stretchr/testify/require"
)
func Test_encrypt_decrypt(t *testing.T) {
tkn := token.NewBearerToken()
box := NewBearerBox(tkn)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
data, err := box.Marshal()
require.NoError(t, err)
encrypted, err := encrypt(cred.PrivateKey(), cred.PublicKey(), data)
require.NoError(t, err)
decrypted, err := decrypt(cred.PrivateKey(), cred.PublicKey(), encrypted)
require.NoError(t, err)
require.Equal(t, data, decrypted)
}
func Test_encrypt_decrypt_step_by_step(t *testing.T) {
tkn := token.NewBearerToken()
box := NewBearerBox(tkn)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
data, err := box.Marshal()
require.NoError(t, err)
buf := new(bytes.Buffer)
_, err = cred.PublicKey().WriteTo(buf)
require.NoError(t, err)
encrypted, err := encrypt(cred.PrivateKey(), cred.PublicKey(), data)
require.NoError(t, err)
length := len(encrypted)
temp := make([]byte, length+binary.MaxVarintLen64)
size := binary.PutVarint(temp, int64(length))
copy(temp[size:], encrypted)
buf.Write(temp[:length+size])
sender, err := hcs.NewPublicKeyFromReader(buf)
require.NoError(t, err)
require.Equal(t, cred.PublicKey(), sender)
ln, err := binary.ReadVarint(buf)
require.NoError(t, err)
require.Equal(t, int64(length), ln)
enc := make([]byte, ln)
n, err := buf.Read(enc)
require.NoError(t, err)
require.Equal(t, length, n)
require.Equal(t, encrypted, enc)
decrypted, err := decrypt(cred.PrivateKey(), sender, enc)
require.NoError(t, err)
require.Equal(t, data, decrypted)
}
func TestSingleKey_AccessBox(t *testing.T) {
tkn := token.NewBearerToken()
expect := NewBearerBox(tkn)
actual := NewBearerBox(nil)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
data, err := Encode(expect, cred.PrivateKey(), cred.PublicKey())
require.NoError(t, err)
require.NoError(t, Decode(data, actual, cred.PrivateKey()))
require.Equal(t, expect, actual)
}
func TestBearerToken_AccessBox(t *testing.T) {
tkn := token.NewBearerToken()
box := NewBearerBox(tkn)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
count := 10
pubs := make([]hcs.PublicKey, 0, count)
keys := make([]hcs.PrivateKey, 0, count)
{ // generate keys
for i := 0; i < count; i++ {
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
pubs = append(pubs, cred.PublicKey())
keys = append(keys, cred.PrivateKey())
}
}
buf := new(bytes.Buffer)
require.NoError(t, NewEncoder(buf, cred.PrivateKey(), pubs...).Encode(box))
data := buf.Bytes()
for i := range keys {
key := keys[i]
t.Run("try with key "+strconv.Itoa(i), func(t *testing.T) {
r := bytes.NewReader(data)
nbx := NewBearerBox(nil)
require.NoError(t, NewDecoder(r, key).Decode(nbx))
require.Equal(t, tkn, nbx.Token())
})
}
t.Run("should fail for unknown key", func(t *testing.T) {
cred, err = hcs.Generate(rand.Reader)
require.NoError(t, err)
r := bytes.NewReader(data)
nbx := NewBearerBox(nil)
require.EqualError(t, NewDecoder(r, cred.PrivateKey()).Decode(nbx), "chacha20poly1305: message authentication failed")
})
}

View file

@ -0,0 +1,88 @@
package accessbox
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
)
type decoder struct {
*bufio.Reader
key hcs.PrivateKey
}
// NewDecoder returns new private key decoder.
func NewDecoder(r io.Reader, key hcs.PrivateKey) Decoder {
return &decoder{Reader: bufio.NewReader(r), key: key}
}
func decrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
sb := sender.Bytes()
key, err := curve25519.X25519(owner.Bytes(), sb)
if err != nil {
return nil, err
}
dec, err := chacha20poly1305.NewX(key)
if err != nil {
return nil, err
}
if ld, ns := len(data), dec.NonceSize(); ld < ns {
return nil, fmt.Errorf("wrong data size (%d), should be greater than %d", ld, ns)
}
nonce, cypher := data[:dec.NonceSize()], data[dec.NonceSize():]
return dec.Open(nil, nonce, cypher, nil)
}
func (d *decoder) Decode(box Box) error {
sender, err := hcs.NewPublicKeyFromReader(d)
if err != nil {
return err
}
var lastErr error
for {
size, err := binary.ReadVarint(d)
if err == io.EOF {
break
} else if err != nil {
return err
}
data := make([]byte, size)
if ln, err := d.Read(data); err != nil {
lastErr = err
continue
} else if ln != int(size) {
lastErr = fmt.Errorf("expect %d bytes, but read only %d bytes", size, ln)
continue
} else if decoded, err := decrypt(d.key, sender, data); err != nil {
lastErr = err
continue
} else if err = box.Unmarshal(decoded); err != nil {
lastErr = err
continue
}
return nil
}
return lastErr
}
// Decode unwraps serialized bearer token from data into box using owner key.
func Decode(data []byte, box Box, owner hcs.PrivateKey) error {
return NewDecoder(bytes.NewBuffer(data), owner).Decode(box)
}

View file

@ -0,0 +1,85 @@
package accessbox
import (
"bytes"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/curve25519"
)
type encoder struct {
io.Writer
owner hcs.PrivateKey
keys []hcs.PublicKey
}
// NewEncoder creates encoder.
func NewEncoder(w io.Writer, owner hcs.PrivateKey, keys ...hcs.PublicKey) Encoder {
return &encoder{
Writer: w,
owner: owner,
keys: keys,
}
}
func encrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
key, err := curve25519.X25519(owner.Bytes(), sender.Bytes())
if err != nil {
return nil, err
}
enc, err := chacha20poly1305.NewX(key)
if err != nil {
return nil, err
}
nonce := make([]byte, enc.NonceSize(), enc.NonceSize()+len(data)+enc.Overhead())
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
return enc.Seal(nonce, nonce, data, nil), nil
}
// Encode and encrypt box through owner private key and public keys.
func (e *encoder) Encode(box Box) error {
data, err := box.Marshal()
if err != nil {
return err
}
// write owner public key
if _, err = e.owner.PublicKey().WriteTo(e); err != nil {
return err
}
for i, sender := range e.keys {
encrypted, err := encrypt(e.owner, sender, data)
if err != nil {
return fmt.Errorf("%w, sender = %d", err, i)
}
ln := len(encrypted)
temp := make([]byte, ln+binary.MaxVarintLen64)
size := binary.PutVarint(temp, int64(ln))
copy(temp[size:], encrypted)
if _, err := e.Write(temp[:size+ln]); err != nil {
return fmt.Errorf("%w, sender = %d", err, i)
}
}
return nil
}
// Encode and encrypt box through owner private key and public keys.
func Encode(box Box, owner hcs.PrivateKey, keys ...hcs.PublicKey) ([]byte, error) {
buf := new(bytes.Buffer)
err := NewEncoder(buf, owner, keys...).Encode(box)
return buf.Bytes(), err
}

140
creds/bearer/credentials.go Normal file
View file

@ -0,0 +1,140 @@
package bearer
import (
"bytes"
"context"
"errors"
"strconv"
"sync"
"time"
"github.com/nspcc-dev/neofs-api-go/pkg/client"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
sdk "github.com/nspcc-dev/neofs-http-gw/neofs"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
)
type (
// Credentials is a bearer token get/put interface.
Credentials interface {
Get(context.Context, *object.Address) (*token.BearerToken, error)
Put(context.Context, *container.ID, *token.BearerToken, ...hcs.PublicKey) (*object.Address, error)
}
cred struct {
key hcs.PrivateKey
obj sdk.ClientPlant
}
)
var (
// ErrEmptyPublicKeys is returned when no HCS keys are provided.
ErrEmptyPublicKeys = errors.New("HCS public keys could not be empty")
// ErrEmptyBearerToken is returned when no bearer token is provided.
ErrEmptyBearerToken = errors.New("Bearer token could not be empty")
)
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
var _ = New
// New creates new Credentials instance using given cli and key.
func New(cli sdk.ClientPlant, key hcs.PrivateKey) Credentials {
return &cred{obj: cli, key: key}
}
func (c *cred) acquireBuffer() *bytes.Buffer {
return bufferPool.Get().(*bytes.Buffer)
}
func (c *cred) releaseBuffer(buf *bytes.Buffer) {
buf.Reset()
bufferPool.Put(buf)
}
func (c *cred) Get(ctx context.Context, address *object.Address) (*token.BearerToken, error) {
buf := c.acquireBuffer()
defer c.releaseBuffer(buf)
box := accessbox.NewBearerBox(nil)
conn, tok, err := c.obj.ConnectionArtifacts()
if err != nil {
return nil, err
}
ops := new(client.GetObjectParams).WithAddress(address).WithPayloadWriter(buf)
_, err = conn.GetObject(
ctx,
ops,
client.WithSession(tok),
)
if err != nil {
return nil, err
}
err = accessbox.NewDecoder(buf, c.key).Decode(box)
if err != nil {
return nil, err
}
return box.Token(), nil
}
func (c *cred) Put(ctx context.Context, cid *container.ID, tkn *token.BearerToken, keys ...hcs.PublicKey) (*object.Address, error) {
var (
err error
buf = c.acquireBuffer()
box = accessbox.NewBearerBox(tkn)
created = strconv.FormatInt(time.Now().Unix(), 10)
)
defer c.releaseBuffer(buf)
if len(keys) == 0 {
return nil, ErrEmptyPublicKeys
} else if tkn == nil {
return nil, ErrEmptyBearerToken
} else if err = accessbox.NewEncoder(buf, c.key, keys...).Encode(box); err != nil {
return nil, err
}
conn, tok, err := c.obj.ConnectionArtifacts()
if err != nil {
return nil, err
}
timestamp := object.NewAttribute()
timestamp.SetKey(object.AttributeTimestamp)
timestamp.SetValue(created)
filename := object.NewAttribute()
filename.SetKey(object.AttributeFileName)
filename.SetValue(created + "_access.box")
raw := object.NewRaw()
raw.SetContainerID(cid)
raw.SetOwnerID(tkn.Issuer())
raw.SetAttributes(filename, timestamp)
ops := new(client.PutObjectParams).WithObject(raw.Object()).WithPayloadReader(buf)
oid, err := conn.PutObject(
ctx,
ops,
client.WithSession(tok),
)
if err != nil {
return nil, err
}
address := object.NewAddress()
address.SetObjectID(oid)
address.SetContainerID(cid)
return address, nil
}

90
creds/hcs/credentials.go Normal file
View file

@ -0,0 +1,90 @@
package hcs
import (
"errors"
"io"
"golang.org/x/crypto/curve25519"
)
type (
// Credentials is an HCS interface (private/public key).
Credentials interface {
PublicKey() PublicKey
PrivateKey() PrivateKey
}
keyer interface {
io.WriterTo
Bytes() []byte
String() string
}
// PublicKey is a public key wrapper providing useful methods.
PublicKey interface {
keyer
}
// PrivateKey is private key wrapper providing useful methods.
PrivateKey interface {
keyer
PublicKey() PublicKey
}
credentials struct {
public PublicKey
secret PrivateKey
}
public []byte
secret []byte
)
// ErrEmptyCredentials is returned when no credentials are provided.
var ErrEmptyCredentials = errors.New("empty credentials")
var _ = NewCredentials
// Generate generates new key pair using given source of randomness.
func Generate(r io.Reader) (Credentials, error) {
buf := make([]byte, curve25519.ScalarSize)
if _, err := r.Read(buf); err != nil {
return nil, err
}
sk := secret(buf)
return &credentials{
secret: &sk,
public: sk.PublicKey(),
}, nil
}
// NewCredentials loads private key from the string given and returns Credentials wrapper.
func NewCredentials(val string) (Credentials, error) {
if val == "" {
return nil, ErrEmptyCredentials
}
sk, err := loadPrivateKey(val)
if err != nil {
return nil, err
}
return &credentials{
secret: sk,
public: sk.PublicKey(),
}, nil
}
// PublicKey returns public key.
func (c *credentials) PublicKey() PublicKey {
return c.public
}
// PrivateKey returns private key.
func (c *credentials) PrivateKey() PrivateKey {
return c.secret
}

65
creds/hcs/public.go Normal file
View file

@ -0,0 +1,65 @@
package hcs
import (
"encoding/hex"
"io"
"io/ioutil"
"os"
"golang.org/x/crypto/curve25519"
)
func (p *public) Bytes() []byte {
buf := make([]byte, curve25519.PointSize)
copy(buf, *p)
return buf
}
func (p *public) String() string {
buf := p.Bytes()
return hex.EncodeToString(buf)
}
func (p *public) WriteTo(w io.Writer) (int64, error) {
pb := p.Bytes()
pl, err := w.Write(pb)
return int64(pl), err
}
func publicKeyFromBytes(v []byte) (PublicKey, error) {
pub := public(v)
return &pub, nil
}
func publicKeyFromString(val string) (PublicKey, error) {
v, err := hex.DecodeString(val)
if err != nil {
return nil, err
}
return publicKeyFromBytes(v)
}
// NewPublicKeyFromReader reads new public key from given reader.
func NewPublicKeyFromReader(r io.Reader) (PublicKey, error) {
data := make([]byte, curve25519.PointSize)
if _, err := r.Read(data); err != nil {
return nil, err
}
return publicKeyFromBytes(data)
}
// LoadPublicKey loads public key from given file or (serialized) string.
func LoadPublicKey(val string) (PublicKey, error) {
data, err := ioutil.ReadFile(val)
if err != nil {
if os.IsNotExist(err) {
return publicKeyFromString(val)
}
return nil, err
}
return publicKeyFromBytes(data)
}

60
creds/hcs/secret.go Normal file
View file

@ -0,0 +1,60 @@
package hcs
import (
"encoding/hex"
"io"
"io/ioutil"
"os"
"golang.org/x/crypto/curve25519"
)
func (s *secret) Bytes() []byte {
buf := make([]byte, curve25519.ScalarSize)
copy(buf, *s)
return buf
}
func (s *secret) String() string {
buf := s.Bytes()
return hex.EncodeToString(buf)
}
func (s *secret) PublicKey() PublicKey {
sk := s.Bytes()
pb, _ := curve25519.X25519(sk, curve25519.Basepoint)
pk := public(pb)
return &pk
}
func (s *secret) WriteTo(w io.Writer) (int64, error) {
sb := s.Bytes()
sl, err := w.Write(sb)
return int64(sl), err
}
func privateKeyFromBytes(val []byte) (PrivateKey, error) {
sk := secret(val)
return &sk, nil
}
func privateKeyFromString(val string) (PrivateKey, error) {
data, err := hex.DecodeString(val)
if err != nil {
return nil, err
}
return privateKeyFromBytes(data)
}
func loadPrivateKey(val string) (PrivateKey, error) {
data, err := ioutil.ReadFile(val)
if os.IsNotExist(err) {
return privateKeyFromString(val)
} else if err != nil {
return nil, err
}
return privateKeyFromBytes(data)
}

View file

@ -0,0 +1,71 @@
package neofs
import (
"crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
crypto "github.com/nspcc-dev/neofs-crypto"
)
type (
// Credentials contains methods that needed to work with NeoFS.
Credentials interface {
WIF() string
Owner() *owner.ID
PublicKey() *ecdsa.PublicKey
PrivateKey() *ecdsa.PrivateKey
}
cred struct {
key *ecdsa.PrivateKey
owner *owner.ID
wif string
}
)
// New creates an instance of Credentials through string representation of secret.
// It allows passing WIF, path, hex-encoded and others.
func New(secret string) (Credentials, error) {
key, err := crypto.LoadPrivateKey(secret)
if err != nil {
return nil, err
}
return setFromPrivateKey(key)
}
// PrivateKey returns ecdsa.PrivateKey.
func (c *cred) PrivateKey() *ecdsa.PrivateKey {
return c.key
}
// PublicKey returns ecdsa.PublicKey.
func (c *cred) PublicKey() *ecdsa.PublicKey {
return &c.key.PublicKey
}
// Owner returns owner.ID.
func (c *cred) Owner() *owner.ID {
return c.owner
}
// WIF returns string representation of WIF.
func (c *cred) WIF() string {
return c.wif
}
func setFromPrivateKey(key *ecdsa.PrivateKey) (*cred, error) {
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
if err != nil {
return nil, err
}
ownerID := owner.NewIDFromNeo3Wallet(wallet)
wif, err := crypto.WIFEncode(key)
if err != nil {
return nil, err
}
return &cred{key: key, owner: ownerID, wif: wif}, nil
}

View file

@ -0,0 +1,40 @@
package neofs
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/stretchr/testify/require"
)
func TestNew(t *testing.T) {
t.Run("should fail", func(t *testing.T) {
cred, err := New("")
require.Nil(t, cred)
require.Error(t, err)
})
t.Run("should work as expected", func(t *testing.T) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
wif, err := crypto.WIFEncode(key)
require.NoError(t, err)
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
require.NoError(t, err)
own := owner.NewIDFromNeo3Wallet(wallet)
cred, err := New(wif)
require.NoError(t, err)
require.Equal(t, cred.WIF(), wif)
require.Equal(t, cred.Owner(), own)
require.Equal(t, cred.PrivateKey(), key)
require.Equal(t, cred.PublicKey(), &key.PublicKey)
})
}

10
go.mod
View file

@ -1,13 +1,14 @@
module github.com/nspcc-dev/neofs-s3-gw module github.com/nspcc-dev/neofs-s3-gw
go 1.14 go 1.16
require ( require (
github.com/aws/aws-sdk-go v1.37.9 github.com/aws/aws-sdk-go v1.37.9
github.com/google/uuid v1.2.0 github.com/google/uuid v1.2.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/nspcc-dev/cdn-sdk v0.3.4 github.com/nspcc-dev/neofs-api-go v1.26.1
github.com/nspcc-dev/neofs-api-go v1.23.0 github.com/nspcc-dev/neofs-crypto v0.3.0
github.com/nspcc-dev/neofs-http-gw v0.15.1
github.com/nspcc-dev/neofs-node v1.22.0 github.com/nspcc-dev/neofs-node v1.22.0
github.com/prometheus/client_golang v1.9.0 github.com/prometheus/client_golang v1.9.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
@ -15,5 +16,6 @@ require (
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.16.0 go.uber.org/zap v1.16.0
google.golang.org/grpc v1.35.0 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
google.golang.org/grpc v1.36.1
) )

51
go.sum
View file

@ -40,6 +40,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -122,6 +124,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp/router v1.3.5/go.mod h1:BylQKgvh6YQkR0mvL60+HJyTaGwcn5d8UFNweOb/Nw8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
@ -256,7 +259,9 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
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.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -317,27 +322,30 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nspcc-dev/cdn-sdk v0.3.4 h1:RtYWuF9xDWrkVwu6sFRWlyZ+ToYM7Y9h8B93Fg4CPEA=
github.com/nspcc-dev/cdn-sdk v0.3.4/go.mod h1:JC4dT16H5HilyZcb8sTxL/TMC1FSEKMuFAqRsmAPoAk=
github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw=
github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY=
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E=
github.com/nspcc-dev/dbft v0.0.0-20201221101812-e13a1a1c3cb2/go.mod h1:I5D0W3tu3epdt2RMCTxS//HDr4S+OHRqajouQTOAHI8= github.com/nspcc-dev/dbft v0.0.0-20201221101812-e13a1a1c3cb2/go.mod h1:I5D0W3tu3epdt2RMCTxS//HDr4S+OHRqajouQTOAHI8=
github.com/nspcc-dev/dbft v0.0.0-20210302103605-cc75991b7cfb/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
github.com/nspcc-dev/neo-go v0.91.0/go.mod h1:G6HdOWvzQ6tlvFdvFSN/PgCzLPN/X/X4d5hTjFRUDcc= github.com/nspcc-dev/neo-go v0.91.0/go.mod h1:G6HdOWvzQ6tlvFdvFSN/PgCzLPN/X/X4d5hTjFRUDcc=
github.com/nspcc-dev/neo-go v0.92.0 h1:iKHpKLzpwE6RSXnQb0BoYWi+H1P/hNyQbMpPG0mY57Q=
github.com/nspcc-dev/neo-go v0.92.0/go.mod h1:L7PyTzjK1j/PCAxvbKiVFkCMZDvsv82JbXlPxaH1t0Q= github.com/nspcc-dev/neo-go v0.92.0/go.mod h1:L7PyTzjK1j/PCAxvbKiVFkCMZDvsv82JbXlPxaH1t0Q=
github.com/nspcc-dev/neo-go v0.95.0 h1:bttArYkIuhBJWSZsZ1xVW8MJsj5SvZwAhqVN3HZPNbo=
github.com/nspcc-dev/neo-go v0.95.0/go.mod h1:bW07ge1WFXsBgqrcPpLUr6OcyQxHqM26MZNesWMdH0c=
github.com/nspcc-dev/neofs-api-go v1.22.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= github.com/nspcc-dev/neofs-api-go v1.22.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8=
github.com/nspcc-dev/neofs-api-go v1.23.0 h1:t4FB5uVY99UkYR0Hiyi1SHjZuqzf4qicw7tf7BBnkHk= github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8=
github.com/nspcc-dev/neofs-api-go v1.23.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8= github.com/nspcc-dev/neofs-api-go v1.26.1 h1:GMIuEB6Hv9IXP9SJd/1f8Df6gRriPkSplpmpJXgQ/1I=
github.com/nspcc-dev/neofs-api-go v1.26.1/go.mod h1:SHuH1Ba3U/h3j+8HHbb3Cns1LfMlEb88guWog9Qi68Y=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-http-gw v0.15.1 h1:0T0g3LeFw5XsxKeWSASNtPOpz0eblhdXmx8coIES5gw=
github.com/nspcc-dev/neofs-http-gw v0.15.1/go.mod h1:DWrjzP7ozKolQkRKYeXrWPzQjrh+GxkFwc4N0yfE/zg=
github.com/nspcc-dev/neofs-node v1.22.0 h1:TJ4d5zopItYYWMEajegVWBgAw8HjZFe12IkNm3Tt+rk= github.com/nspcc-dev/neofs-node v1.22.0 h1:TJ4d5zopItYYWMEajegVWBgAw8HjZFe12IkNm3Tt+rk=
github.com/nspcc-dev/neofs-node v1.22.0/go.mod h1:ecpXrzIe1vcp5FBjPsIaHKVIVvxsv4GVBCw21WYcY3c= github.com/nspcc-dev/neofs-node v1.22.0/go.mod h1:ecpXrzIe1vcp5FBjPsIaHKVIVvxsv4GVBCw21WYcY3c=
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
@ -427,6 +435,7 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/savsgio/gotils v0.0.0-20210105085219-0567298fdcac/go.mod h1:TWNAOTaVzGOXq8RbEvHnhzA/A2sLZzgn0m6URjnukY8=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -484,6 +493,11 @@ github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.19.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A=
github.com/valyala/fasthttp v1.22.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
@ -528,8 +542,9 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -571,10 +586,12 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226101413-39120d07d75e/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -609,24 +626,30 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/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.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/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-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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -697,8 +720,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 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.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.36.1 h1:cmUfbeGKnz9+2DD/UYsMQXeqbHZqZDs4eQwW0sFOpBY=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=