// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package plugin import ( "context" "errors" "google.golang.org/grpc" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/plugin/pb" ) var errMissingStorage = errors.New("missing storage implementation: this method should not be called during plugin Setup, but only during and after Initialize") func newGRPCStorageClient(conn *grpc.ClientConn) *GRPCStorageClient { return &GRPCStorageClient{ client: pb.NewStorageClient(conn), } } // GRPCStorageClient is an implementation of logical.Storage that communicates // over RPC. type GRPCStorageClient struct { client pb.StorageClient } func (s *GRPCStorageClient) List(ctx context.Context, prefix string) ([]string, error) { reply, err := s.client.List(ctx, &pb.StorageListArgs{ Prefix: prefix, }, largeMsgGRPCCallOpts...) if err != nil { return []string{}, err } if reply.Err != "" { return reply.Keys, errors.New(reply.Err) } return reply.Keys, nil } func (s *GRPCStorageClient) Get(ctx context.Context, key string) (*logical.StorageEntry, error) { reply, err := s.client.Get(ctx, &pb.StorageGetArgs{ Key: key, }, largeMsgGRPCCallOpts...) if err != nil { return nil, err } if reply.Err != "" { return nil, errors.New(reply.Err) } return pb.ProtoStorageEntryToLogicalStorageEntry(reply.Entry), nil } func (s *GRPCStorageClient) Put(ctx context.Context, entry *logical.StorageEntry) error { reply, err := s.client.Put(ctx, &pb.StoragePutArgs{ Entry: pb.LogicalStorageEntryToProtoStorageEntry(entry), }, largeMsgGRPCCallOpts...) if err != nil { return err } if reply.Err != "" { return errors.New(reply.Err) } return nil } func (s *GRPCStorageClient) Delete(ctx context.Context, key string) error { reply, err := s.client.Delete(ctx, &pb.StorageDeleteArgs{ Key: key, }) if err != nil { return err } if reply.Err != "" { return errors.New(reply.Err) } return nil } // GRPCStorageServer is a net/rpc compatible structure for serving type GRPCStorageServer struct { pb.UnimplementedStorageServer impl logical.Storage } func (s *GRPCStorageServer) List(ctx context.Context, args *pb.StorageListArgs) (*pb.StorageListReply, error) { if s.impl == nil { return nil, errMissingStorage } keys, err := s.impl.List(ctx, args.Prefix) return &pb.StorageListReply{ Keys: keys, Err: pb.ErrToString(err), }, nil } func (s *GRPCStorageServer) Get(ctx context.Context, args *pb.StorageGetArgs) (*pb.StorageGetReply, error) { if s.impl == nil { return nil, errMissingStorage } storageEntry, err := s.impl.Get(ctx, args.Key) if storageEntry == nil { return &pb.StorageGetReply{ Entry: nil, Err: pb.ErrToString(err), }, nil } return &pb.StorageGetReply{ Entry: pb.LogicalStorageEntryToProtoStorageEntry(storageEntry), Err: pb.ErrToString(err), }, nil } func (s *GRPCStorageServer) Put(ctx context.Context, args *pb.StoragePutArgs) (*pb.StoragePutReply, error) { if s.impl == nil { return nil, errMissingStorage } err := s.impl.Put(ctx, pb.ProtoStorageEntryToLogicalStorageEntry(args.Entry)) return &pb.StoragePutReply{ Err: pb.ErrToString(err), }, nil } func (s *GRPCStorageServer) Delete(ctx context.Context, args *pb.StorageDeleteArgs) (*pb.StorageDeleteReply, error) { if s.impl == nil { return nil, errMissingStorage } err := s.impl.Delete(ctx, args.Key) return &pb.StorageDeleteReply{ Err: pb.ErrToString(err), }, nil } // NOOPStorage is used to deny access to the storage interface while running a // backend plugin in metadata mode. type NOOPStorage struct{} func (s *NOOPStorage) List(_ context.Context, prefix string) ([]string, error) { return []string{}, nil } func (s *NOOPStorage) Get(_ context.Context, key string) (*logical.StorageEntry, error) { return nil, nil } func (s *NOOPStorage) Put(_ context.Context, entry *logical.StorageEntry) error { return nil } func (s *NOOPStorage) Delete(_ context.Context, key string) error { return nil }