storj/pkg/pointerdb/pointerdb.go
Natalie Villasana c3d3f41d30 removes some SignedMessage use (#1258)
Removes most instances of pb.SignedMessage (there's more to take out but they shouldn't hurt anyone as is).

There used to be places in psserver where a PieceID was hmac'd with the SatelliteID, which was gotten from a SignedMessage. This PR makes it so some functions access the SatelliteID from the Payer Bandwidth Allocation instead.

This requires passing a SatelliteID into psserver functions where they weren't before, so the following proto messages have been changed:

 * PieceId - satellite_id field added
   This is so the psserver.Piece function has access to the SatelliteID when it needs to get the namespaced pieceID.
   This proto message should probably be renamed to PieceRequest, or a new PieceRequest message should be created so this isn't misnamed.

 * PieceDelete - satellite_id field added
   This is so the psserver.Delete function has access to the SatelliteID when receiving a request to Delete.
2019-02-19 23:36:08 -06:00

259 lines
7.4 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pointerdb
import (
"context"
"github.com/zeebo/errs"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/auth"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/overlay"
"storj.io/storj/pkg/pb"
_ "storj.io/storj/pkg/pointerdb/auth" // ensures that we add api key flag to current executable
"storj.io/storj/pkg/storj"
"storj.io/storj/satellite/console"
"storj.io/storj/storage"
)
var (
mon = monkit.Package()
segmentError = errs.Class("segment error")
)
// APIKeys is api keys store methods used by pointerdb
type APIKeys interface {
GetByKey(ctx context.Context, key console.APIKey) (*console.APIKeyInfo, error)
}
// Server implements the network state RPC service
type Server struct {
logger *zap.Logger
service *Service
allocation *AllocationSigner
cache *overlay.Cache
config Config
identity *identity.FullIdentity
apiKeys APIKeys
}
// NewServer creates instance of Server
func NewServer(logger *zap.Logger, service *Service, allocation *AllocationSigner, cache *overlay.Cache, config Config, identity *identity.FullIdentity, apiKeys APIKeys) *Server {
return &Server{
logger: logger,
service: service,
allocation: allocation,
cache: cache,
config: config,
identity: identity,
apiKeys: apiKeys,
}
}
// Close closes resources
func (s *Server) Close() error { return nil }
func (s *Server) validateAuth(ctx context.Context) (*console.APIKeyInfo, error) {
APIKey, ok := auth.GetAPIKey(ctx)
if !ok {
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, "Invalid API credential")))
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
}
key, err := console.APIKeyFromBase64(string(APIKey))
if err != nil {
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, "Invalid API credential")))
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
}
keyInfo, err := s.apiKeys.GetByKey(ctx, *key)
if err != nil {
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, err.Error())))
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
}
return keyInfo, nil
}
func (s *Server) validateSegment(req *pb.PutRequest) error {
min := s.config.MinRemoteSegmentSize
remote := req.GetPointer().Remote
remoteSize := req.GetPointer().GetSegmentSize()
if remote != nil && remoteSize < int64(min) {
return segmentError.New("remote segment size %d less than minimum allowed %d", remoteSize, min)
}
max := s.config.MaxInlineSegmentSize.Int()
inlineSize := len(req.GetPointer().InlineSegment)
if inlineSize > max {
return segmentError.New("inline segment size %d greater than maximum allowed %d", inlineSize, max)
}
return nil
}
// Put formats and hands off a key/value (path/pointer) to be saved to boltdb
func (s *Server) Put(ctx context.Context, req *pb.PutRequest) (resp *pb.PutResponse, err error) {
defer mon.Task()(&ctx)(&err)
err = s.validateSegment(req)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
keyInfo, err := s.validateAuth(ctx)
if err != nil {
return nil, err
}
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
if err = s.service.Put(path, req.GetPointer()); err != nil {
s.logger.Error("err putting pointer", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
return &pb.PutResponse{}, nil
}
// Get formats and hands off a file path to get from boltdb
func (s *Server) Get(ctx context.Context, req *pb.GetRequest) (resp *pb.GetResponse, err error) {
defer mon.Task()(&ctx)(&err)
keyInfo, err := s.validateAuth(ctx)
if err != nil {
return nil, err
}
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
pointer, err := s.service.Get(path)
if err != nil {
if storage.ErrKeyNotFound.Has(err) {
return nil, status.Errorf(codes.NotFound, err.Error())
}
s.logger.Error("err getting pointer", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
pba, err := s.PayerBandwidthAllocation(ctx, &pb.PayerBandwidthAllocationRequest{Action: pb.BandwidthAction_GET})
if err != nil {
s.logger.Error("err getting payer bandwidth allocation", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
nodes := []*pb.Node{}
var r = &pb.GetResponse{
Pointer: pointer,
Nodes: nil,
Pba: pba.GetPba(),
}
if !s.config.Overlay || pointer.Remote == nil {
return r, nil
}
for _, piece := range pointer.Remote.RemotePieces {
node, err := s.cache.Get(ctx, piece.NodeId)
if err != nil {
s.logger.Error("Error getting node from cache", zap.String("ID", piece.NodeId.String()), zap.Error(err))
continue
}
nodes = append(nodes, node)
}
for _, v := range nodes {
if v != nil {
v.Type.DPanicOnInvalid("pdb server Get")
}
}
r = &pb.GetResponse{
Pointer: pointer,
Nodes: nodes,
Pba: pba.GetPba(),
}
return r, nil
}
// List returns all Path keys in the Pointers bucket
func (s *Server) List(ctx context.Context, req *pb.ListRequest) (resp *pb.ListResponse, err error) {
defer mon.Task()(&ctx)(&err)
keyInfo, err := s.validateAuth(ctx)
if err != nil {
return nil, err
}
prefix := storj.JoinPaths(keyInfo.ProjectID.String(), req.Prefix)
items, more, err := s.service.List(prefix, req.StartAfter, req.EndBefore, req.Recursive, req.Limit, req.MetaFlags)
if err != nil {
return nil, status.Errorf(codes.Internal, "ListV2: %v", err)
}
return &pb.ListResponse{Items: items, More: more}, nil
}
// Delete formats and hands off a file path to delete from boltdb
func (s *Server) Delete(ctx context.Context, req *pb.DeleteRequest) (resp *pb.DeleteResponse, err error) {
defer mon.Task()(&ctx)(&err)
keyInfo, err := s.validateAuth(ctx)
if err != nil {
return nil, err
}
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
err = s.service.Delete(path)
if err != nil {
s.logger.Error("err deleting path and pointer", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
return &pb.DeleteResponse{}, nil
}
// Iterate iterates over items based on IterateRequest
func (s *Server) Iterate(ctx context.Context, req *pb.IterateRequest, f func(it storage.Iterator) error) (err error) {
defer mon.Task()(&ctx)(&err)
keyInfo, err := s.validateAuth(ctx)
if err != nil {
return err
}
prefix := storj.JoinPaths(keyInfo.ProjectID.String(), req.Prefix)
return s.service.Iterate(prefix, req.First, req.Recurse, req.Reverse, f)
}
// PayerBandwidthAllocation returns PayerBandwidthAllocation struct, signed and with given action type
func (s *Server) PayerBandwidthAllocation(ctx context.Context, req *pb.PayerBandwidthAllocationRequest) (res *pb.PayerBandwidthAllocationResponse, err error) {
defer mon.Task()(&ctx)(&err)
_, err = s.validateAuth(ctx)
if err != nil {
return nil, err
}
// TODO(michal) should be replaced with renter id when available
// retrieve the public key
pi, err := identity.PeerIdentityFromContext(ctx)
if err != nil {
return nil, err
}
pba, err := s.allocation.PayerBandwidthAllocation(ctx, pi, req.GetAction())
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &pb.PayerBandwidthAllocationResponse{Pba: pba}, nil
}