frostfs-api-go/pkg/client/container.go
Alex Vanin de505ddee9 [#133] sdk: Use cid wrapper to sign container delete request
StableMarshalerWrapper is not suitable for container delete
request, because it signs marshaled structure of container id.
As for delete request, container id itself should be signed.

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
2020-09-18 10:45:11 +03:00

338 lines
8.5 KiB
Go

package client
import (
"context"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-api-go/util/signature"
"github.com/nspcc-dev/neofs-api-go/v2/client"
v2container "github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/pkg/errors"
)
type delContainerSignWrapper struct {
body *v2container.DeleteRequestBody
}
func (c delContainerSignWrapper) ReadSignedData(bytes []byte) ([]byte, error) {
return c.body.GetContainerID().GetValue(), nil
}
func (c delContainerSignWrapper) SignedDataSize() int {
return len(c.body.GetContainerID().GetValue())
}
func (c Client) PutContainer(ctx context.Context, cnr *container.Container, opts ...CallOption) (*container.ID, error) {
switch c.remoteNode.Version.Major {
case 2:
return c.putContainerV2(ctx, cnr, opts...)
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) GetContainer(ctx context.Context, id *container.ID, opts ...CallOption) (*container.Container, error) {
switch c.remoteNode.Version.Major {
case 2:
return c.getContainerV2(ctx, id, opts...)
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) ListContainers(ctx context.Context, owner *owner.ID, opts ...CallOption) ([]*container.ID, error) {
switch c.remoteNode.Version.Major {
case 2:
return c.listContainerV2(ctx, owner, opts...)
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) ListSelfContainers(ctx context.Context, opts ...CallOption) ([]*container.ID, error) {
w, err := owner.NEO3WalletFromPublicKey(&c.key.PublicKey)
if err != nil {
return nil, err
}
ownerID := new(owner.ID)
ownerID.SetNeo3Wallet(w)
return c.ListContainers(ctx, ownerID, opts...)
}
func (c Client) DeleteContainer(ctx context.Context, id *container.ID, opts ...CallOption) error {
switch c.remoteNode.Version.Major {
case 2:
return c.delContainerV2(ctx, id, opts...)
default:
return unsupportedProtocolErr
}
}
// todo: func (c Client) GetExtendedACL
// todo: func (c Client) SetExtendedACL
func (c Client) putContainerV2(ctx context.Context, cnr *container.Container, opts ...CallOption) (*container.ID, error) {
// apply all available options
callOptions := defaultCallOptions()
for i := range opts {
opts[i].apply(&callOptions)
}
// set transport version
cnr.SetVersion(c.remoteNode.Version.ToV2Version())
// if container owner is not set, then use client key as owner
if cnr.GetOwnerID() == nil {
w, err := owner.NEO3WalletFromPublicKey(&c.key.PublicKey)
if err != nil {
return nil, err
}
ownerID := new(owner.ID)
ownerID.SetNeo3Wallet(w)
cnr.SetOwnerID(ownerID.ToV2())
}
reqBody := new(v2container.PutRequestBody)
reqBody.SetContainer(&cnr.Container)
// sign container
signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetContainer()}
err := signature.SignDataWithHandler(c.key, signWrapper, func(key []byte, sig []byte) {
containerSignature := new(refs.Signature)
containerSignature.SetKey(key)
containerSignature.SetSign(sig)
reqBody.SetSignature(containerSignature)
}, signature.SignWithRFC6979())
if err != nil {
return nil, err
}
req := new(v2container.PutRequest)
req.SetBody(reqBody)
req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions))
err = v2signature.SignServiceMessage(c.key, req)
if err != nil {
return nil, err
}
switch c.remoteNode.Protocol {
case GRPC:
cli, err := v2ContainerClientFromOptions(c.opts)
if err != nil {
return nil, errors.Wrap(err, "can't create grpc client")
}
resp, err := cli.Put(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "transport error")
}
err = v2signature.VerifyServiceMessage(resp)
if err != nil {
return nil, errors.Wrap(err, "can't verify response message")
}
cidV2 := resp.GetBody().GetContainerID()
cid, err := container.IDFromV2(cidV2)
if err != nil {
return nil, errors.Wrapf(err, "could not convert %T to %T", cidV2, cid)
}
return cid, nil
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) getContainerV2(ctx context.Context, id *container.ID, opts ...CallOption) (*container.Container, error) {
// apply all available options
callOptions := defaultCallOptions()
for i := range opts {
opts[i].apply(&callOptions)
}
reqBody := new(v2container.GetRequestBody)
reqBody.SetContainerID(id.ToV2())
req := new(v2container.GetRequest)
req.SetBody(reqBody)
req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions))
err := v2signature.SignServiceMessage(c.key, req)
if err != nil {
return nil, err
}
switch c.remoteNode.Protocol {
case GRPC:
cli, err := v2ContainerClientFromOptions(c.opts)
if err != nil {
return nil, errors.Wrap(err, "can't create grpc client")
}
resp, err := cli.Get(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "transport error")
}
err = v2signature.VerifyServiceMessage(resp)
if err != nil {
return nil, errors.Wrap(err, "can't verify response message")
}
return &container.Container{
Container: *resp.GetBody().GetContainer(),
}, nil
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) listContainerV2(ctx context.Context, owner *owner.ID, opts ...CallOption) ([]*container.ID, error) {
// apply all available options
callOptions := defaultCallOptions()
for i := range opts {
opts[i].apply(&callOptions)
}
reqBody := new(v2container.ListRequestBody)
reqBody.SetOwnerID(owner.ToV2())
req := new(v2container.ListRequest)
req.SetBody(reqBody)
req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions))
err := v2signature.SignServiceMessage(c.key, req)
if err != nil {
return nil, err
}
switch c.remoteNode.Protocol {
case GRPC:
cli, err := v2ContainerClientFromOptions(c.opts)
if err != nil {
return nil, errors.Wrap(err, "can't create grpc client")
}
resp, err := cli.List(ctx, req)
if err != nil {
return nil, errors.Wrap(err, "transport error")
}
err = v2signature.VerifyServiceMessage(resp)
if err != nil {
return nil, errors.Wrap(err, "can't verify response message")
}
result := make([]*container.ID, 0, len(resp.GetBody().GetContainerIDs()))
for _, cidV2 := range resp.GetBody().GetContainerIDs() {
cid, err := container.IDFromV2(cidV2)
if err != nil {
return nil, errors.Wrapf(err, "could not convert %T to %T", cidV2, cid)
}
result = append(result, cid)
}
return result, nil
default:
return nil, unsupportedProtocolErr
}
}
func (c Client) delContainerV2(ctx context.Context, id *container.ID, opts ...CallOption) error {
// apply all available options
callOptions := defaultCallOptions()
for i := range opts {
opts[i].apply(&callOptions)
}
reqBody := new(v2container.DeleteRequestBody)
reqBody.SetContainerID(id.ToV2())
// sign container
err := signature.SignDataWithHandler(c.key,
delContainerSignWrapper{
body: reqBody,
},
func(key []byte, sig []byte) {
containerSignature := new(refs.Signature)
containerSignature.SetKey(key)
containerSignature.SetSign(sig)
reqBody.SetSignature(containerSignature)
},
signature.SignWithRFC6979())
if err != nil {
return err
}
req := new(v2container.DeleteRequest)
req.SetBody(reqBody)
req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions))
err = v2signature.SignServiceMessage(c.key, req)
if err != nil {
return err
}
switch c.remoteNode.Protocol {
case GRPC:
cli, err := v2ContainerClientFromOptions(c.opts)
if err != nil {
return errors.Wrap(err, "can't create grpc client")
}
resp, err := cli.Delete(ctx, req)
if err != nil {
return errors.Wrap(err, "transport error")
}
err = v2signature.VerifyServiceMessage(resp)
if err != nil {
return errors.Wrap(err, "can't verify response message")
}
return nil
default:
return unsupportedProtocolErr
}
}
func v2ContainerClientFromOptions(opts *clientOptions) (cli *v2container.Client, err error) {
switch {
case opts.grpcOpts.v2ContainerClient != nil:
// return value from client cache
return opts.grpcOpts.v2ContainerClient, nil
case opts.grpcOpts.conn != nil:
cli, err = v2container.NewClient(v2container.WithGlobalOpts(
client.WithGRPCConn(opts.grpcOpts.conn)),
)
case opts.addr != "":
cli, err = v2container.NewClient(v2container.WithGlobalOpts(
client.WithNetworkAddress(opts.addr)),
)
default:
return nil, errors.New("lack of sdk client options to create accounting client")
}
// check if client correct and save in cache
if err != nil {
return nil, err
}
opts.grpcOpts.v2ContainerClient = cli
return cli, nil
}