Use context to propagate API Key (#383)

* Satellite signs proofs

* wip

* remove direct apikey usage from pdbclient

* adjusting unit tests

* fix linter errors

* unit tests

* linter errors

* remove usless interface

* remove unused code

* improve unit tests

* signature generation

* code review changes

* code review comments

* back to satellite-id signature generation

* remove go-grpc-middlewar dependency

* small step back

* linter fixes

* fix tests

* packages reorganization

* Move TestAPIKeyInjector to grpcauth package
This commit is contained in:
Michal Niewrzal 2018-10-09 16:39:14 +02:00 committed by GitHub
parent a4b5978259
commit ad327bedb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 324 additions and 196 deletions

View File

@ -21,6 +21,7 @@ import (
"storj.io/storj/pkg/pointerdb"
"storj.io/storj/pkg/process"
"storj.io/storj/pkg/provider"
"storj.io/storj/pkg/auth/grpcauth"
)
const (
@ -94,7 +95,7 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
go func(i int, farmer string) {
_, _ = fmt.Printf("starting farmer %d %s (kad on %s)\n", i, farmer,
runCfg.StorageNodes[i].Kademlia.TODOListenAddr)
errch <- runCfg.StorageNodes[i].Identity.Run(ctx,
errch <- runCfg.StorageNodes[i].Identity.Run(ctx, nil,
runCfg.StorageNodes[i].Kademlia,
runCfg.StorageNodes[i].Storage)
}(i, storagenode)
@ -108,7 +109,9 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
if runCfg.Satellite.MockOverlay.Enabled {
o = mock.Config{Nodes: strings.Join(storagenodes, ",")}
}
errch <- runCfg.Satellite.Identity.Run(ctx,
grpcauth.NewAPIKeyInterceptor(),
runCfg.Satellite.PointerDB,
runCfg.Satellite.Kademlia,
// runCfg.Satellite.Checker,

View File

@ -19,6 +19,7 @@ import (
"storj.io/storj/pkg/pointerdb"
"storj.io/storj/pkg/process"
"storj.io/storj/pkg/provider"
"storj.io/storj/pkg/auth/grpcauth"
"storj.io/storj/pkg/statdb"
)
@ -73,8 +74,14 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
if runCfg.MockOverlay.Nodes != "" {
o = runCfg.MockOverlay
}
return runCfg.Identity.Run(process.Ctx(cmd),
runCfg.Kademlia, runCfg.PointerDB, o, runCfg.StatDB)
return runCfg.Identity.Run(
process.Ctx(cmd),
grpcauth.NewAPIKeyInterceptor(),
runCfg.Kademlia,
runCfg.PointerDB,
o,
runCfg.StatDB,
)
}
func cmdSetup(cmd *cobra.Command, args []string) (err error) {

View File

@ -54,7 +54,7 @@ func init() {
}
func cmdRun(cmd *cobra.Command, args []string) (err error) {
return runCfg.Identity.Run(process.Ctx(cmd), runCfg.Kademlia, runCfg.Storage)
return runCfg.Identity.Run(process.Ctx(cmd), nil, runCfg.Kademlia, runCfg.Storage)
}
func cmdSetup(cmd *cobra.Command, args []string) (err error) {

View File

@ -47,7 +47,7 @@ func main() {
logger.Error("Failed to create full identity: ", zap.Error(err))
os.Exit(1)
}
APIKey := []byte("abc123")
APIKey := "abc123"
client, err := pdbclient.NewClient(identity, pointerdbClientPort, APIKey)
if err != nil {

24
pkg/auth/apikey.go Normal file
View File

@ -0,0 +1,24 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package auth
import "context"
// The key type is unexported to prevent collisions with context keys defined in
// other packages.
type key int
// apiKey is the context key for the user API Key
const apiKey key = 0
// WithAPIKey creates context with api key
func WithAPIKey(ctx context.Context, key []byte) context.Context {
return context.WithValue(ctx, apiKey, key)
}
// GetAPIKey returns api key from context is exists
func GetAPIKey(ctx context.Context) ([]byte, bool) {
key, ok := ctx.Value(apiKey).([]byte)
return key, ok
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package grpcauth
import (
"context"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"storj.io/storj/pkg/auth"
)
// NewAPIKeyInterceptor creates instance of apikey interceptor
func NewAPIKeyInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{},
err error) {
md, ok := metadata.FromIncomingContext(ctx)
APIKey := strings.Join(md["apikey"], "")
if !ok || APIKey == "" {
return handler(ctx, req)
}
return handler(auth.WithAPIKey(ctx, []byte(APIKey)), req)
}
}
// NewAPIKeyInjector injects api key to grpc connection context
func NewAPIKeyInjector(APIKey string) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
ctx = metadata.AppendToOutgoingContext(ctx, "apikey", APIKey)
return invoker(ctx, method, req, reply, cc)
}
}

View File

@ -0,0 +1,77 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package grpcauth
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"storj.io/storj/pkg/auth"
)
func TestAPIKeyInterceptor(t *testing.T) {
for _, tt := range []struct {
APIKey string
err error
}{
{"", status.Errorf(codes.Unauthenticated, "Invalid API credential")},
{"good key", nil},
{"wrong key", status.Errorf(codes.Unauthenticated, "Invalid API credential")},
} {
interceptor := NewAPIKeyInterceptor()
// mock for method handler
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
APIKey, ok := auth.GetAPIKey(ctx)
if !ok || string(APIKey) != "good key" {
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
}
return nil, nil
}
ctx := context.Background()
ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("apikey", tt.APIKey))
info := &grpc.UnaryServerInfo{}
_, err := interceptor(ctx, nil, info, handler)
assert.Equal(t, err, tt.err)
}
}
func TestAPIKeyInjector(t *testing.T) {
for _, tt := range []struct {
APIKey string
err error
}{
{"abc123", nil},
{"", nil},
} {
injector := NewAPIKeyInjector(tt.APIKey)
// mock for method invoker
var outputCtx context.Context
invoker := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
outputCtx = ctx
return nil
}
ctx := context.Background()
err := injector(ctx, "/test.method", nil, nil, nil, invoker)
assert.Equal(t, err, tt.err)
md, ok := metadata.FromOutgoingContext(outputCtx)
assert.Equal(t, true, ok)
assert.Equal(t, tt.APIKey, strings.Join(md["apikey"], ""))
}
}

View File

@ -133,7 +133,7 @@ func (c Config) GetBucketStore(ctx context.Context, identity *provider.FullIdent
return nil, err
}
pdb, err := pdbclient.NewClient(identity, c.PointerDBAddr, []byte(c.APIKey))
pdb, err := pdbclient.NewClient(identity, c.PointerDBAddr, c.APIKey)
if err != nil {
return nil, err
}

View File

@ -33,7 +33,6 @@ const (
var RedundancyScheme_SchemeType_name = map[int32]string{
0: "RS",
}
var RedundancyScheme_SchemeType_value = map[string]int32{
"RS": 0,
}
@ -41,9 +40,8 @@ var RedundancyScheme_SchemeType_value = map[string]int32{
func (x RedundancyScheme_SchemeType) String() string {
return proto.EnumName(RedundancyScheme_SchemeType_name, int32(x))
}
func (RedundancyScheme_SchemeType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{0, 0}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{0, 0}
}
type EncryptionScheme_EncryptionType int32
@ -57,7 +55,6 @@ var EncryptionScheme_EncryptionType_name = map[int32]string{
0: "AESGCM",
1: "SECRETBOX",
}
var EncryptionScheme_EncryptionType_value = map[string]int32{
"AESGCM": 0,
"SECRETBOX": 1,
@ -66,9 +63,8 @@ var EncryptionScheme_EncryptionType_value = map[string]int32{
func (x EncryptionScheme_EncryptionType) String() string {
return proto.EnumName(EncryptionScheme_EncryptionType_name, int32(x))
}
func (EncryptionScheme_EncryptionType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{1, 0}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{1, 0}
}
type Pointer_DataType int32
@ -82,7 +78,6 @@ var Pointer_DataType_name = map[int32]string{
0: "INLINE",
1: "REMOTE",
}
var Pointer_DataType_value = map[string]int32{
"INLINE": 0,
"REMOTE": 1,
@ -91,9 +86,8 @@ var Pointer_DataType_value = map[string]int32{
func (x Pointer_DataType) String() string {
return proto.EnumName(Pointer_DataType_name, int32(x))
}
func (Pointer_DataType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{4, 0}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{4, 0}
}
type RedundancyScheme struct {
@ -113,7 +107,7 @@ func (m *RedundancyScheme) Reset() { *m = RedundancyScheme{} }
func (m *RedundancyScheme) String() string { return proto.CompactTextString(m) }
func (*RedundancyScheme) ProtoMessage() {}
func (*RedundancyScheme) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{0}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{0}
}
func (m *RedundancyScheme) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RedundancyScheme.Unmarshal(m, b)
@ -188,7 +182,7 @@ func (m *EncryptionScheme) Reset() { *m = EncryptionScheme{} }
func (m *EncryptionScheme) String() string { return proto.CompactTextString(m) }
func (*EncryptionScheme) ProtoMessage() {}
func (*EncryptionScheme) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{1}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{1}
}
func (m *EncryptionScheme) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EncryptionScheme.Unmarshal(m, b)
@ -241,7 +235,7 @@ func (m *RemotePiece) Reset() { *m = RemotePiece{} }
func (m *RemotePiece) String() string { return proto.CompactTextString(m) }
func (*RemotePiece) ProtoMessage() {}
func (*RemotePiece) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{2}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{2}
}
func (m *RemotePiece) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RemotePiece.Unmarshal(m, b)
@ -289,7 +283,7 @@ func (m *RemoteSegment) Reset() { *m = RemoteSegment{} }
func (m *RemoteSegment) String() string { return proto.CompactTextString(m) }
func (*RemoteSegment) ProtoMessage() {}
func (*RemoteSegment) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{3}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{3}
}
func (m *RemoteSegment) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RemoteSegment.Unmarshal(m, b)
@ -354,7 +348,7 @@ func (m *Pointer) Reset() { *m = Pointer{} }
func (m *Pointer) String() string { return proto.CompactTextString(m) }
func (*Pointer) ProtoMessage() {}
func (*Pointer) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{4}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{4}
}
func (m *Pointer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Pointer.Unmarshal(m, b)
@ -427,7 +421,6 @@ func (m *Pointer) GetMetadata() []byte {
type PutRequest struct {
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
Pointer *Pointer `protobuf:"bytes,2,opt,name=pointer,proto3" json:"pointer,omitempty"`
APIKey []byte `protobuf:"bytes,3,opt,name=API_key,json=APIKey,proto3" json:"API_key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -437,7 +430,7 @@ func (m *PutRequest) Reset() { *m = PutRequest{} }
func (m *PutRequest) String() string { return proto.CompactTextString(m) }
func (*PutRequest) ProtoMessage() {}
func (*PutRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{5}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{5}
}
func (m *PutRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutRequest.Unmarshal(m, b)
@ -471,17 +464,9 @@ func (m *PutRequest) GetPointer() *Pointer {
return nil
}
func (m *PutRequest) GetAPIKey() []byte {
if m != nil {
return m.APIKey
}
return nil
}
// GetRequest is a request message for the Get rpc call
type GetRequest struct {
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
APIKey []byte `protobuf:"bytes,2,opt,name=API_key,json=APIKey,proto3" json:"API_key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -491,7 +476,7 @@ func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{6}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{6}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
@ -518,13 +503,6 @@ func (m *GetRequest) GetPath() string {
return ""
}
func (m *GetRequest) GetAPIKey() []byte {
if m != nil {
return m.APIKey
}
return nil
}
// ListRequest is a request message for the List rpc call
type ListRequest struct {
Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"`
@ -533,7 +511,6 @@ type ListRequest struct {
Recursive bool `protobuf:"varint,4,opt,name=recursive,proto3" json:"recursive,omitempty"`
Limit int32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"`
MetaFlags uint32 `protobuf:"fixed32,6,opt,name=meta_flags,json=metaFlags,proto3" json:"meta_flags,omitempty"`
APIKey []byte `protobuf:"bytes,7,opt,name=API_key,json=APIKey,proto3" json:"API_key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -543,7 +520,7 @@ func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{7}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{7}
}
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b)
@ -605,13 +582,6 @@ func (m *ListRequest) GetMetaFlags() uint32 {
return 0
}
func (m *ListRequest) GetAPIKey() []byte {
if m != nil {
return m.APIKey
}
return nil
}
// PutResponse is a response message for the Put rpc call
type PutResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -623,7 +593,7 @@ func (m *PutResponse) Reset() { *m = PutResponse{} }
func (m *PutResponse) String() string { return proto.CompactTextString(m) }
func (*PutResponse) ProtoMessage() {}
func (*PutResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{8}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{8}
}
func (m *PutResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PutResponse.Unmarshal(m, b)
@ -656,7 +626,7 @@ func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{9}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{9}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
@ -703,7 +673,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{10}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{10}
}
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b)
@ -750,7 +720,7 @@ func (m *ListResponse_Item) Reset() { *m = ListResponse_Item{} }
func (m *ListResponse_Item) String() string { return proto.CompactTextString(m) }
func (*ListResponse_Item) ProtoMessage() {}
func (*ListResponse_Item) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{10, 0}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{10, 0}
}
func (m *ListResponse_Item) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse_Item.Unmarshal(m, b)
@ -793,7 +763,6 @@ func (m *ListResponse_Item) GetIsPrefix() bool {
type DeleteRequest struct {
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
APIKey []byte `protobuf:"bytes,2,opt,name=API_key,json=APIKey,proto3" json:"API_key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -803,7 +772,7 @@ func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
func (m *DeleteRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteRequest) ProtoMessage() {}
func (*DeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{11}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{11}
}
func (m *DeleteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteRequest.Unmarshal(m, b)
@ -830,13 +799,6 @@ func (m *DeleteRequest) GetPath() string {
return ""
}
func (m *DeleteRequest) GetAPIKey() []byte {
if m != nil {
return m.APIKey
}
return nil
}
// DeleteResponse is a response message for the Delete rpc call
type DeleteResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -848,7 +810,7 @@ func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_75fef806d28fc810, []int{12}
return fileDescriptor_pointerdb_7ab81da76529f96d, []int{12}
}
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteResponse.Unmarshal(m, b)
@ -1067,72 +1029,70 @@ var _PointerDB_serviceDesc = grpc.ServiceDesc{
Metadata: "pointerdb.proto",
}
func init() { proto.RegisterFile("pointerdb.proto", fileDescriptor_75fef806d28fc810) }
func init() { proto.RegisterFile("pointerdb.proto", fileDescriptor_pointerdb_7ab81da76529f96d) }
var fileDescriptor_75fef806d28fc810 = []byte{
// 1013 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdb, 0x6e, 0xdb, 0x46,
0x10, 0x35, 0x75, 0xe7, 0xc8, 0xb2, 0xd9, 0x45, 0xea, 0x30, 0x72, 0x8a, 0x18, 0x2c, 0x5a, 0xb8,
0x4d, 0x20, 0x17, 0x6a, 0x80, 0x5e, 0xd2, 0x0b, 0x7c, 0x51, 0x0d, 0x21, 0x89, 0x23, 0xac, 0xfc,
0x50, 0xf4, 0x85, 0xa5, 0xc5, 0xb1, 0xbc, 0x88, 0x78, 0xf1, 0xee, 0x32, 0x88, 0xf2, 0x27, 0xfd,
0x98, 0x3e, 0xf6, 0x03, 0xfa, 0x21, 0x45, 0x1f, 0xfa, 0x03, 0xc5, 0xee, 0x92, 0x12, 0x69, 0xb7,
0x09, 0x50, 0xf4, 0xc5, 0xe6, 0x9c, 0x39, 0x33, 0xb3, 0x73, 0x66, 0x76, 0x05, 0xdb, 0x69, 0xc2,
0x62, 0x89, 0x3c, 0xbc, 0x18, 0xa4, 0x3c, 0x91, 0x09, 0xb1, 0x57, 0x40, 0xff, 0xc1, 0x3c, 0x49,
0xe6, 0x0b, 0x3c, 0xd0, 0x8e, 0x8b, 0xec, 0xf2, 0x40, 0xb2, 0x08, 0x85, 0x0c, 0xa2, 0xd4, 0x70,
0xfb, 0xbd, 0xe4, 0x15, 0xf2, 0x45, 0xb0, 0x34, 0xa6, 0xf7, 0x4b, 0x0d, 0x1c, 0x8a, 0x61, 0x16,
0x87, 0x41, 0x3c, 0x5b, 0x4e, 0x67, 0x57, 0x18, 0x21, 0xf9, 0x1a, 0x1a, 0x72, 0x99, 0xa2, 0x6b,
0xed, 0x59, 0xfb, 0x5b, 0xc3, 0x8f, 0x07, 0xeb, 0x7a, 0x37, 0xa9, 0x03, 0xf3, 0xef, 0x7c, 0x99,
0x22, 0xd5, 0x31, 0xe4, 0x2e, 0xb4, 0x23, 0x16, 0xfb, 0x1c, 0xaf, 0xdd, 0xda, 0x9e, 0xb5, 0xdf,
0xa4, 0xad, 0x88, 0xc5, 0x14, 0xaf, 0xc9, 0x1d, 0x68, 0xca, 0x44, 0x06, 0x0b, 0xb7, 0xae, 0x61,
0x63, 0x90, 0x4f, 0xc0, 0xe1, 0x98, 0x06, 0x8c, 0xfb, 0xf2, 0x8a, 0xa3, 0xb8, 0x4a, 0x16, 0xa1,
0xdb, 0xd0, 0x84, 0x6d, 0x83, 0x9f, 0x17, 0x30, 0x79, 0x08, 0xef, 0x89, 0x6c, 0x36, 0x43, 0x21,
0x4a, 0xdc, 0xa6, 0xe6, 0x3a, 0xb9, 0x63, 0x4d, 0x7e, 0x04, 0x04, 0x79, 0x20, 0x32, 0x8e, 0xbe,
0xb8, 0x0a, 0xd4, 0x5f, 0xf6, 0x06, 0xdd, 0x96, 0x61, 0xe7, 0x9e, 0xa9, 0x72, 0x4c, 0xd9, 0x1b,
0xf4, 0xee, 0x00, 0xac, 0x1b, 0x21, 0x2d, 0xa8, 0xd1, 0xa9, 0xb3, 0xe1, 0xfd, 0x65, 0x81, 0x33,
0x8a, 0x67, 0x7c, 0x99, 0x4a, 0x96, 0xc4, 0xb9, 0x36, 0xdf, 0x55, 0xb4, 0xf9, 0xb4, 0xa4, 0xcd,
0x4d, 0x6a, 0x09, 0x28, 0xe9, 0xf3, 0x25, 0xb8, 0x68, 0x70, 0x0c, 0x7d, 0x5c, 0x31, 0xfc, 0x97,
0xb8, 0xd4, 0x82, 0x6d, 0xd2, 0x9d, 0x95, 0x7f, 0x9d, 0xe0, 0x29, 0x2e, 0xab, 0x91, 0x42, 0x06,
0x5c, 0xb2, 0x78, 0xee, 0xc7, 0x49, 0x3c, 0x43, 0xad, 0x69, 0x39, 0x72, 0x9a, 0xbb, 0xcf, 0x94,
0xd7, 0x7b, 0x08, 0x5b, 0xd5, 0xb3, 0x10, 0x80, 0xd6, 0xe1, 0x68, 0x7a, 0x7a, 0xfc, 0xdc, 0xd9,
0x20, 0x3d, 0xb0, 0xa7, 0xa3, 0x63, 0x3a, 0x3a, 0x3f, 0x7a, 0xf1, 0xa3, 0x63, 0x79, 0xc7, 0xd0,
0xa5, 0x18, 0x25, 0x12, 0x27, 0x0c, 0x67, 0x48, 0x76, 0xc1, 0x4e, 0xd5, 0x87, 0x1f, 0x67, 0x91,
0x6e, 0xba, 0x49, 0x3b, 0x1a, 0x38, 0xcb, 0x22, 0x35, 0xec, 0x38, 0x09, 0xd1, 0x67, 0xa1, 0x3e,
0xbb, 0x4d, 0x5b, 0xca, 0x1c, 0x87, 0xde, 0x6f, 0x16, 0xf4, 0x4c, 0x96, 0x29, 0xce, 0x23, 0x8c,
0x25, 0x79, 0x02, 0xc0, 0x57, 0xcb, 0xa3, 0x13, 0x75, 0x87, 0xbb, 0x6f, 0xd9, 0x2c, 0x5a, 0xa2,
0x93, 0x7b, 0x60, 0x6a, 0xae, 0x0b, 0xb5, 0xb5, 0x3d, 0x0e, 0xc9, 0x13, 0xe8, 0x71, 0x5d, 0xc8,
0xd7, 0x88, 0x70, 0xeb, 0x7b, 0xf5, 0xfd, 0xee, 0x70, 0xa7, 0x92, 0x7a, 0xd5, 0x0e, 0xdd, 0xe4,
0x6b, 0x43, 0x90, 0x07, 0xd0, 0x8d, 0x90, 0xbf, 0x5c, 0xa0, 0xcf, 0x93, 0x44, 0xea, 0xc5, 0xdb,
0xa4, 0x60, 0x20, 0x9a, 0x24, 0xd2, 0xfb, 0xa3, 0x06, 0xed, 0x89, 0x49, 0x44, 0x0e, 0x2a, 0x93,
0x2f, 0x9f, 0x3d, 0x67, 0x0c, 0x4e, 0x02, 0x19, 0x94, 0x46, 0xfd, 0x11, 0x6c, 0xb1, 0x78, 0xc1,
0x62, 0xf4, 0x85, 0x11, 0x21, 0x1f, 0x53, 0xcf, 0xa0, 0x85, 0x32, 0x9f, 0x41, 0xcb, 0x1c, 0x4a,
0xd7, 0xef, 0x0e, 0xdd, 0x5b, 0x47, 0xcf, 0x99, 0x34, 0xe7, 0x11, 0x02, 0x0d, 0xbd, 0xce, 0x6a,
0xf9, 0xeb, 0x54, 0x7f, 0x93, 0xef, 0xa1, 0x37, 0xe3, 0x18, 0xe8, 0x5d, 0x0a, 0x03, 0x69, 0x76,
0xbd, 0x3b, 0xec, 0x0f, 0xcc, 0x83, 0x30, 0x28, 0x1e, 0x84, 0xc1, 0x79, 0xf1, 0x20, 0xd0, 0xcd,
0x22, 0xe0, 0x24, 0x90, 0x48, 0x8e, 0x61, 0x1b, 0x5f, 0xa7, 0x8c, 0x97, 0x52, 0xb4, 0xdf, 0x99,
0x62, 0x6b, 0x1d, 0xa2, 0x93, 0xf4, 0xa1, 0x13, 0xa1, 0x0c, 0xc2, 0x40, 0x06, 0x6e, 0x47, 0x37,
0xbb, 0xb2, 0x3d, 0x0f, 0x3a, 0x85, 0x40, 0x6a, 0xff, 0xc6, 0x67, 0xcf, 0xc6, 0x67, 0x23, 0x67,
0x43, 0x7d, 0xd3, 0xd1, 0xf3, 0x17, 0xe7, 0x23, 0xc7, 0xf2, 0xe6, 0x00, 0x93, 0x4c, 0x52, 0xbc,
0xce, 0x50, 0x48, 0xd5, 0x67, 0x1a, 0xc8, 0x2b, 0xad, 0xb8, 0x4d, 0xf5, 0x37, 0x79, 0x04, 0xed,
0x5c, 0x1e, 0xbd, 0x09, 0xdd, 0x21, 0xb9, 0x3d, 0x08, 0x5a, 0x50, 0xd4, 0x82, 0x1e, 0x4e, 0xc6,
0xfa, 0x72, 0x19, 0xed, 0x5b, 0x87, 0x93, 0xf1, 0x53, 0x5c, 0x7a, 0x5f, 0x01, 0x9c, 0xe2, 0x5b,
0x0b, 0x95, 0x42, 0x6b, 0x95, 0xd0, 0xdf, 0x2d, 0xe8, 0x3e, 0x63, 0x62, 0x15, 0xbc, 0x03, 0xad,
0x94, 0xe3, 0x25, 0x7b, 0x9d, 0x87, 0xe7, 0x96, 0x5a, 0x2e, 0x7d, 0x4b, 0xfd, 0xe0, 0xb2, 0x38,
0xad, 0x4d, 0x41, 0x43, 0x87, 0x0a, 0x21, 0x1f, 0x00, 0x60, 0x1c, 0xfa, 0x17, 0x78, 0x99, 0x70,
0x73, 0x85, 0x6d, 0x6a, 0x63, 0x1c, 0x1e, 0x69, 0x80, 0xdc, 0x07, 0x9b, 0xe3, 0x2c, 0xe3, 0x82,
0xbd, 0x32, 0xab, 0xd1, 0xa1, 0x6b, 0x40, 0x3d, 0xa7, 0x0b, 0x16, 0x31, 0x99, 0xbf, 0x80, 0xc6,
0x50, 0x29, 0x95, 0xde, 0xfe, 0xe5, 0x22, 0x98, 0x0b, 0xbd, 0x02, 0x6d, 0x6a, 0x2b, 0xe4, 0x07,
0x05, 0x94, 0x7b, 0x6a, 0x57, 0x7a, 0xea, 0x41, 0x57, 0xeb, 0x2e, 0xd2, 0x24, 0x16, 0xe8, 0xfd,
0x0c, 0x5d, 0xad, 0x8e, 0x31, 0xcb, 0x9a, 0x5b, 0xef, 0xd6, 0xfc, 0x43, 0x68, 0xaa, 0x57, 0x40,
0xb8, 0x35, 0x7d, 0x13, 0x7b, 0x83, 0xe2, 0x17, 0xe7, 0x2c, 0x09, 0x91, 0x1a, 0x9f, 0xf7, 0xab,
0x05, 0x9b, 0x46, 0xc4, 0xbc, 0xc6, 0x10, 0x9a, 0x4c, 0x62, 0x24, 0x5c, 0x4b, 0x47, 0xdd, 0x2f,
0x55, 0x28, 0xf3, 0x06, 0x63, 0x89, 0x11, 0x35, 0x54, 0x35, 0xb6, 0x48, 0x49, 0x57, 0xd3, 0xe2,
0xe8, 0xef, 0x3e, 0x42, 0x43, 0x51, 0xfe, 0x87, 0xdd, 0xd9, 0x05, 0x9b, 0x09, 0x3f, 0x1f, 0x6d,
0x5d, 0x97, 0xe8, 0x30, 0x31, 0xd1, 0xb6, 0xf7, 0x0d, 0xf4, 0x4e, 0x70, 0x81, 0x12, 0xff, 0xd3,
0x0a, 0x39, 0xb0, 0x55, 0x44, 0x9b, 0xb6, 0x86, 0x7f, 0x5a, 0x60, 0xe7, 0x27, 0x38, 0x39, 0x22,
0x8f, 0xa1, 0x3e, 0xc9, 0x24, 0x79, 0xbf, 0x7c, 0xbc, 0xd5, 0xb5, 0xe8, 0xef, 0xdc, 0x84, 0x73,
0x09, 0x1f, 0x43, 0xfd, 0x14, 0xab, 0x51, 0xeb, 0x1d, 0xaf, 0x44, 0x95, 0x87, 0xfb, 0x05, 0x34,
0x94, 0xc0, 0x64, 0xe7, 0x96, 0xe2, 0x26, 0xee, 0xee, 0xbf, 0x4c, 0x82, 0x7c, 0x0b, 0x2d, 0xd3,
0x04, 0x29, 0xbf, 0x58, 0x15, 0x55, 0xfa, 0xf7, 0xfe, 0xc1, 0x63, 0xc2, 0x8f, 0x1a, 0x3f, 0xd5,
0xd2, 0x8b, 0x8b, 0x96, 0x7e, 0x54, 0x3e, 0xff, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x69, 0xc5,
0xee, 0xd4, 0x08, 0x00, 0x00,
var fileDescriptor_pointerdb_7ab81da76529f96d = []byte{
// 987 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xcd, 0x6e, 0xdb, 0x46,
0x10, 0x36, 0xf5, 0xcf, 0xa1, 0x65, 0xb3, 0x8b, 0xd4, 0x61, 0x94, 0x14, 0x31, 0x18, 0xb4, 0x70,
0x9b, 0x40, 0x2e, 0xd4, 0x00, 0x2d, 0x1a, 0xb4, 0x45, 0x6c, 0xab, 0x86, 0xd1, 0x44, 0x31, 0x56,
0x3e, 0x14, 0xbd, 0xb0, 0xb4, 0x38, 0x92, 0x16, 0x11, 0x7f, 0xbc, 0xbb, 0x0c, 0xa2, 0xbc, 0x49,
0xdf, 0xa4, 0x97, 0x1e, 0xfb, 0x38, 0x45, 0x0f, 0x7d, 0x81, 0x62, 0x77, 0x49, 0x89, 0xb2, 0x5b,
0xe7, 0x90, 0x8b, 0xcd, 0xf9, 0xe6, 0x9b, 0x99, 0x9d, 0x6f, 0x66, 0x57, 0xb0, 0x9b, 0xa5, 0x2c,
0x91, 0xc8, 0xa3, 0xcb, 0x7e, 0xc6, 0x53, 0x99, 0x12, 0x7b, 0x05, 0xf4, 0x1e, 0xce, 0xd2, 0x74,
0xb6, 0xc0, 0x43, 0xed, 0xb8, 0xcc, 0xa7, 0x87, 0x92, 0xc5, 0x28, 0x64, 0x18, 0x67, 0x86, 0xdb,
0xeb, 0xa6, 0x6f, 0x90, 0x2f, 0xc2, 0xa5, 0x31, 0xfd, 0xdf, 0x6a, 0xe0, 0x52, 0x8c, 0xf2, 0x24,
0x0a, 0x93, 0xc9, 0x72, 0x3c, 0x99, 0x63, 0x8c, 0xe4, 0x5b, 0x68, 0xc8, 0x65, 0x86, 0x9e, 0xb5,
0x6f, 0x1d, 0xec, 0x0c, 0x3e, 0xeb, 0xaf, 0xeb, 0x5d, 0xa7, 0xf6, 0xcd, 0xbf, 0x8b, 0x65, 0x86,
0x54, 0xc7, 0x90, 0xbb, 0xd0, 0x8e, 0x59, 0x12, 0x70, 0xbc, 0xf2, 0x6a, 0xfb, 0xd6, 0x41, 0x93,
0xb6, 0x62, 0x96, 0x50, 0xbc, 0x22, 0x77, 0xa0, 0x29, 0x53, 0x19, 0x2e, 0xbc, 0xba, 0x86, 0x8d,
0x41, 0x3e, 0x07, 0x97, 0x63, 0x16, 0x32, 0x1e, 0xc8, 0x39, 0x47, 0x31, 0x4f, 0x17, 0x91, 0xd7,
0xd0, 0x84, 0x5d, 0x83, 0x5f, 0x94, 0x30, 0x79, 0x0c, 0x1f, 0x89, 0x7c, 0x32, 0x41, 0x21, 0x2a,
0xdc, 0xa6, 0xe6, 0xba, 0x85, 0x63, 0x4d, 0x7e, 0x02, 0x04, 0x79, 0x28, 0x72, 0x8e, 0x81, 0x98,
0x87, 0xea, 0x2f, 0x7b, 0x87, 0x5e, 0xcb, 0xb0, 0x0b, 0xcf, 0x58, 0x39, 0xc6, 0xec, 0x1d, 0xfa,
0x77, 0x00, 0xd6, 0x8d, 0x90, 0x16, 0xd4, 0xe8, 0xd8, 0xdd, 0xf2, 0xff, 0xb1, 0xc0, 0x1d, 0x26,
0x13, 0xbe, 0xcc, 0x24, 0x4b, 0x93, 0x42, 0x9b, 0xef, 0x37, 0xb4, 0xf9, 0xa2, 0xa2, 0xcd, 0x75,
0x6a, 0x05, 0xa8, 0xe8, 0xf3, 0x0d, 0x78, 0x68, 0x70, 0x8c, 0x02, 0x5c, 0x31, 0x82, 0xd7, 0xb8,
0xd4, 0x82, 0x6d, 0xd3, 0xbd, 0x95, 0x7f, 0x9d, 0xe0, 0x27, 0x5c, 0x6e, 0x46, 0x0a, 0x19, 0x72,
0xc9, 0x92, 0x59, 0x90, 0xa4, 0xc9, 0x04, 0xb5, 0xa6, 0xd5, 0xc8, 0x71, 0xe1, 0x1e, 0x29, 0xaf,
0xff, 0x18, 0x76, 0x36, 0xcf, 0x42, 0x00, 0x5a, 0xcf, 0x87, 0xe3, 0xd3, 0xe3, 0x97, 0xee, 0x16,
0xe9, 0x82, 0x3d, 0x1e, 0x1e, 0xd3, 0xe1, 0xc5, 0xd1, 0xab, 0x9f, 0x5d, 0xcb, 0x3f, 0x06, 0x87,
0x62, 0x9c, 0x4a, 0x3c, 0x67, 0x38, 0x41, 0x72, 0x1f, 0xec, 0x4c, 0x7d, 0x04, 0x49, 0x1e, 0xeb,
0xa6, 0x9b, 0xb4, 0xa3, 0x81, 0x51, 0x1e, 0xab, 0x61, 0x27, 0x69, 0x84, 0x01, 0x8b, 0xf4, 0xd9,
0x6d, 0xda, 0x52, 0xe6, 0x59, 0xe4, 0xff, 0x69, 0x41, 0xd7, 0x64, 0x19, 0xe3, 0x2c, 0xc6, 0x44,
0x92, 0x67, 0x00, 0x7c, 0xb5, 0x3c, 0x3a, 0x91, 0x33, 0xb8, 0x7f, 0xcb, 0x66, 0xd1, 0x0a, 0x9d,
0xdc, 0x03, 0x53, 0x73, 0x5d, 0xa8, 0xad, 0xed, 0xb3, 0x88, 0x3c, 0x83, 0x2e, 0xd7, 0x85, 0x02,
0x8d, 0x08, 0xaf, 0xbe, 0x5f, 0x3f, 0x70, 0x06, 0x7b, 0x1b, 0xa9, 0x57, 0xed, 0xd0, 0x6d, 0xbe,
0x36, 0x04, 0x79, 0x08, 0x4e, 0x8c, 0xfc, 0xf5, 0x02, 0x03, 0x9e, 0xa6, 0x52, 0x2f, 0xde, 0x36,
0x05, 0x03, 0xd1, 0x34, 0x95, 0xfe, 0x5f, 0x35, 0x68, 0x9f, 0x9b, 0x44, 0xe4, 0x70, 0x63, 0xf2,
0xd5, 0xb3, 0x17, 0x8c, 0xfe, 0x49, 0x28, 0xc3, 0xca, 0xa8, 0x3f, 0x85, 0x1d, 0x96, 0x2c, 0x58,
0x82, 0x81, 0x30, 0x22, 0x14, 0x63, 0xea, 0x1a, 0xb4, 0x54, 0xe6, 0x4b, 0x68, 0x99, 0x43, 0xe9,
0xfa, 0xce, 0xc0, 0xbb, 0x71, 0xf4, 0x82, 0x49, 0x0b, 0x1e, 0x21, 0xd0, 0xd0, 0xeb, 0xac, 0x96,
0xbf, 0x4e, 0xf5, 0x37, 0xf9, 0x01, 0xba, 0x13, 0x8e, 0xa1, 0xde, 0xa5, 0x28, 0x94, 0x66, 0xd7,
0x9d, 0x41, 0xaf, 0x6f, 0x1e, 0x84, 0x7e, 0xf9, 0x20, 0xf4, 0x2f, 0xca, 0x07, 0x81, 0x6e, 0x97,
0x01, 0x27, 0xa1, 0x44, 0x72, 0x0c, 0xbb, 0xf8, 0x36, 0x63, 0xbc, 0x92, 0xa2, 0xfd, 0xde, 0x14,
0x3b, 0xeb, 0x10, 0x9d, 0xa4, 0x07, 0x9d, 0x18, 0x65, 0x18, 0x85, 0x32, 0xf4, 0x3a, 0xba, 0xd9,
0x95, 0xed, 0xfb, 0xd0, 0x29, 0x05, 0x52, 0xfb, 0x77, 0x36, 0x7a, 0x71, 0x36, 0x1a, 0xba, 0x5b,
0xea, 0x9b, 0x0e, 0x5f, 0xbe, 0xba, 0x18, 0xba, 0x96, 0x3f, 0x02, 0x38, 0xcf, 0x25, 0xc5, 0xab,
0x1c, 0x85, 0x54, 0x7d, 0x66, 0xa1, 0x9c, 0x6b, 0xc5, 0x6d, 0xaa, 0xbf, 0xc9, 0x13, 0x68, 0x17,
0xf2, 0xe8, 0x4d, 0x70, 0x06, 0xe4, 0xe6, 0x20, 0x68, 0x49, 0xf1, 0xf7, 0x01, 0x4e, 0xf1, 0xb6,
0x7c, 0xfe, 0xef, 0x16, 0x38, 0x2f, 0x98, 0x58, 0x71, 0xf6, 0xa0, 0x95, 0x71, 0x9c, 0xb2, 0xb7,
0x05, 0xab, 0xb0, 0xd4, 0xaa, 0xe8, 0x3b, 0x17, 0x84, 0xd3, 0xb2, 0xb6, 0x4d, 0x41, 0x43, 0xcf,
0x15, 0x42, 0x3e, 0x01, 0xc0, 0x24, 0x0a, 0x2e, 0x71, 0x9a, 0x72, 0x73, 0x21, 0x6d, 0x6a, 0x63,
0x12, 0x1d, 0x69, 0x80, 0x3c, 0x00, 0x9b, 0xe3, 0x24, 0xe7, 0x82, 0xbd, 0x31, 0x83, 0xee, 0xd0,
0x35, 0xa0, 0x1e, 0xc7, 0x05, 0x8b, 0x99, 0x2c, 0xde, 0x33, 0x63, 0xa8, 0x94, 0x4a, 0xbd, 0x60,
0xba, 0x08, 0x67, 0x42, 0x0f, 0xb4, 0x4d, 0x6d, 0x85, 0xfc, 0xa8, 0x00, 0xbf, 0x0b, 0x8e, 0x16,
0x4b, 0x64, 0x69, 0x22, 0xd0, 0xff, 0x15, 0x1c, 0xdd, 0xab, 0x31, 0xab, 0x42, 0x59, 0xef, 0x15,
0x8a, 0x3c, 0x82, 0xa6, 0xba, 0xba, 0xc2, 0xab, 0xe9, 0xeb, 0xd3, 0xed, 0x97, 0x3f, 0x13, 0xa3,
0x34, 0x42, 0x6a, 0x7c, 0xfe, 0x1f, 0x16, 0x6c, 0x1b, 0xad, 0x8a, 0x1a, 0x03, 0x68, 0x32, 0x89,
0xb1, 0xf0, 0x2c, 0x1d, 0xf5, 0xa0, 0x52, 0xa1, 0xca, 0xeb, 0x9f, 0x49, 0x8c, 0xa9, 0xa1, 0xaa,
0x21, 0xc4, 0x4a, 0xa1, 0x9a, 0xd6, 0x40, 0x7f, 0xf7, 0x10, 0x1a, 0x8a, 0xf2, 0xe1, 0x03, 0x57,
0xcf, 0x15, 0x13, 0x41, 0x31, 0xc1, 0xba, 0x2e, 0xd1, 0x61, 0xe2, 0x5c, 0xdb, 0xfe, 0x23, 0xe8,
0x9e, 0xe0, 0x02, 0x25, 0xde, 0xb6, 0x10, 0x2e, 0xec, 0x94, 0x24, 0x73, 0xfa, 0xc1, 0xdf, 0x16,
0xd8, 0x45, 0xa1, 0x93, 0x23, 0xf2, 0x14, 0xea, 0xe7, 0xb9, 0x24, 0x1f, 0x57, 0x4f, 0xb1, 0x5a,
0xd9, 0xde, 0xde, 0x75, 0xb8, 0x50, 0xea, 0x29, 0xd4, 0x4f, 0x71, 0x33, 0x6a, 0xbd, 0x98, 0x1b,
0x51, 0xd5, 0x19, 0x7e, 0x0d, 0x0d, 0xa5, 0x23, 0xd9, 0xbb, 0x21, 0xac, 0x89, 0xbb, 0xfb, 0x3f,
0x82, 0x93, 0xef, 0xa0, 0x65, 0x9a, 0x20, 0xd5, 0xd7, 0x64, 0xa3, 0xf9, 0xde, 0xbd, 0xff, 0xf0,
0x98, 0xf0, 0xa3, 0xc6, 0x2f, 0xb5, 0xec, 0xf2, 0xb2, 0xa5, 0x2f, 0xfc, 0x57, 0xff, 0x06, 0x00,
0x00, 0xff, 0xff, 0x73, 0xbc, 0xd6, 0xdd, 0x70, 0x08, 0x00, 0x00,
}

View File

@ -82,13 +82,11 @@ message Pointer {
message PutRequest {
string path = 1;
Pointer pointer = 2;
bytes API_key = 3;
}
// GetRequest is a request message for the Get rpc call
message GetRequest {
string path = 1;
bytes API_key = 2;
}
// ListRequest is a request message for the List rpc call
@ -99,7 +97,6 @@ message ListRequest {
bool recursive = 4;
int32 limit = 5;
fixed32 meta_flags = 6;
bytes API_key = 7;
}
// PutResponse is a response message for the Put rpc call
@ -126,7 +123,6 @@ message ListResponse {
message DeleteRequest {
string path = 1;
bytes API_key = 2;
}
// DeleteResponse is a response message for the Delete rpc call

View File

@ -19,6 +19,7 @@ import (
"storj.io/storj/pkg/pointerdb"
"storj.io/storj/pkg/pointerdb/pdbclient"
"storj.io/storj/pkg/storage/meta"
"storj.io/storj/pkg/auth"
"storj.io/storj/storage/teststore"
"storj.io/storj/storage/redis/redisserver"
@ -74,60 +75,51 @@ func TestAuditSegment(t *testing.T) {
tests := []struct {
bm string
path paths.Path
APIKey []byte
}{
{
bm: "success-1",
path: paths.New("folder1/file1"),
APIKey: nil,
},
{
bm: "success-2",
path: paths.New("foodFolder1/file1/file2"),
APIKey: nil,
},
{
bm: "success-3",
path: paths.New("foodFolder1/file1/file2/foodFolder2/file3"),
APIKey: nil,
},
{
bm: "success-4",
path: paths.New("projectFolder/project1.txt/"),
APIKey: nil,
},
{
bm: "success-5",
path: paths.New("newProjectFolder/project2.txt"),
APIKey: nil,
},
{
bm: "success-6",
path: paths.New("Pictures/image1.png"),
APIKey: nil,
},
{
bm: "success-7",
path: paths.New("Pictures/Nature/mountains.png"),
APIKey: nil,
},
{
bm: "success-8",
path: paths.New("Pictures/City/streets.png"),
APIKey: nil,
},
{
bm: "success-9",
path: paths.New("Pictures/Animals/Dogs/dogs.png"),
APIKey: nil,
},
{
bm: "success-10",
path: paths.New("Nada/ビデオ/😶"),
APIKey: nil,
},
}
ctx = auth.WithAPIKey(ctx, nil)
//PointerDB instantation
db := teststore.New()
c := pointerdb.Config{MaxInlineSegmentSize: 8000}
@ -145,7 +137,7 @@ func TestAuditSegment(t *testing.T) {
assert.NotNil(t, cache)
pdbw := newPointerDBWrapper(pointerdb.NewServer(db, cache, zap.NewNop(), c))
pointers := pdbclient.New(pdbw, nil)
pointers := pdbclient.New(pdbw)
// create a pdb client and instance of audit
a := NewAudit(pointers)
@ -157,10 +149,10 @@ func TestAuditSegment(t *testing.T) {
assert1 := assert.New(t)
// create a pointer and put in db
putRequest := makePointer(tt.path, tt.APIKey)
putRequest := makePointer(tt.path)
// create putreq. object
req := &pb.PutRequest{Path: tt.path.String(), Pointer: putRequest.Pointer, APIKey: tt.APIKey}
req := &pb.PutRequest{Path: tt.path.String(), Pointer: putRequest.Pointer}
//Put pointer into db
_, err := pdbw.Put(ctx, req)
@ -250,7 +242,7 @@ func TestAuditSegment(t *testing.T) {
})
}
func makePointer(path paths.Path, auth []byte) pb.PutRequest {
func makePointer(path paths.Path) pb.PutRequest {
var rps []*pb.RemotePiece
rps = append(rps, &pb.RemotePiece{
PieceNum: 1,
@ -274,7 +266,6 @@ func makePointer(path paths.Path, auth []byte) pb.PutRequest {
},
Size: int64(10),
},
APIKey: auth,
}
return pr
}

View File

@ -11,6 +11,7 @@ import (
"google.golang.org/grpc/status"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/auth/grpcauth"
p "storj.io/storj/pkg/paths"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/provider"
@ -24,15 +25,11 @@ var (
// PointerDB creates a grpcClient
type PointerDB struct {
grpcClient pb.PointerDBClient
APIKey []byte
}
// New Used as a public function
func New(gcclient pb.PointerDBClient, APIKey []byte) (pdbc *PointerDB) {
return &PointerDB{
grpcClient: gcclient,
APIKey: APIKey,
}
func New(gcclient pb.PointerDBClient) (pdbc *PointerDB) {
return &PointerDB{grpcClient: gcclient}
}
// a compiler trick to make sure *Overlay implements Client
@ -56,21 +53,19 @@ type Client interface {
}
// NewClient initializes a new pointerdb client
func NewClient(identity *provider.FullIdentity, address string, APIKey []byte) (*PointerDB, error) {
func NewClient(identity *provider.FullIdentity, address string, APIKey string) (*PointerDB, error) {
dialOpt, err := identity.DialOption()
if err != nil {
return nil, err
}
c, err := clientConnection(address, dialOpt)
apiKeyInjector := grpcauth.NewAPIKeyInjector(APIKey)
c, err := clientConnection(address, dialOpt, grpc.WithUnaryInterceptor(apiKeyInjector))
if err != nil {
return nil, err
}
return &PointerDB{
grpcClient: c,
APIKey: APIKey,
}, nil
return &PointerDB{grpcClient: c}, nil
}
// a compiler trick to make sure *PointerDB implements Client
@ -90,7 +85,7 @@ func clientConnection(serverAddr string, opts ...grpc.DialOption) (pb.PointerDBC
func (pdb *PointerDB) Put(ctx context.Context, path p.Path, pointer *pb.Pointer) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = pdb.grpcClient.Put(ctx, &pb.PutRequest{Path: path.String(), Pointer: pointer, APIKey: pdb.APIKey})
_, err = pdb.grpcClient.Put(ctx, &pb.PutRequest{Path: path.String(), Pointer: pointer})
return err
}
@ -99,7 +94,7 @@ func (pdb *PointerDB) Put(ctx context.Context, path p.Path, pointer *pb.Pointer)
func (pdb *PointerDB) Get(ctx context.Context, path p.Path) (pointer *pb.Pointer, err error) {
defer mon.Task()(&ctx)(&err)
res, err := pdb.grpcClient.Get(ctx, &pb.GetRequest{Path: path.String(), APIKey: pdb.APIKey})
res, err := pdb.grpcClient.Get(ctx, &pb.GetRequest{Path: path.String()})
if err != nil {
if status.Code(err) == codes.NotFound {
return nil, storage.ErrKeyNotFound.Wrap(err)
@ -123,7 +118,6 @@ func (pdb *PointerDB) List(ctx context.Context, prefix, startAfter, endBefore p.
Recursive: recursive,
Limit: int32(limit),
MetaFlags: metaFlags,
APIKey: pdb.APIKey,
})
if err != nil {
return nil, false, err
@ -146,7 +140,7 @@ func (pdb *PointerDB) List(ctx context.Context, prefix, startAfter, endBefore p.
func (pdb *PointerDB) Delete(ctx context.Context, path p.Path) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = pdb.grpcClient.Delete(ctx, &pb.DeleteRequest{Path: path.String(), APIKey: pdb.APIKey})
_, err = pdb.grpcClient.Delete(ctx, &pb.DeleteRequest{Path: path.String()})
return err
}

View File

@ -16,6 +16,7 @@ import (
"github.com/golang/protobuf/ptypes"
"github.com/stretchr/testify/assert"
"storj.io/storj/pkg/auth"
p "storj.io/storj/pkg/paths"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storage/meta"
@ -27,7 +28,6 @@ const (
)
var (
ctx = context.Background()
ErrUnauthenticated = errors.New(unauthenticated)
ErrNoFileGiven = errors.New(noPathGiven)
)
@ -45,7 +45,7 @@ func TestNewPointerDBClient(t *testing.T) {
assert.NotNil(t, pdb.grpcClient)
}
func makePointer(path p.Path, auth []byte) pb.PutRequest {
func makePointer(path p.Path) pb.PutRequest {
// rps is an example slice of RemotePieces to add to this
// REMOTE pointer type.
var rps []*pb.RemotePiece
@ -70,7 +70,6 @@ func makePointer(path p.Path, auth []byte) pb.PutRequest {
},
Size: int64(1),
},
APIKey: auth,
}
return pr
}
@ -91,11 +90,14 @@ func TestPut(t *testing.T) {
{[]byte("wrong key"), p.New(""), ErrUnauthenticated, unauthenticated},
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
} {
putRequest := makePointer(tt.path, tt.APIKey)
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
putRequest := makePointer(tt.path)
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey}
pdb := PointerDB{grpcClient: gc}
// here we don't care what type of context we pass
gc.EXPECT().Put(gomock.Any(), &putRequest).Return(nil, tt.err)
@ -126,8 +128,11 @@ func TestGet(t *testing.T) {
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), p.New("file1/file2"), nil, ""},
} {
getPointer := makePointer(tt.path, tt.APIKey)
getRequest := pb.GetRequest{Path: tt.path.String(), APIKey: tt.APIKey}
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
getPointer := makePointer(tt.path)
getRequest := pb.GetRequest{Path: tt.path.String()}
data, err := proto.Marshal(getPointer.Pointer)
if err != nil {
@ -144,7 +149,7 @@ func TestGet(t *testing.T) {
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey}
pdb := PointerDB{grpcClient: gc}
gc.EXPECT().Get(gomock.Any(), &getRequest).Return(&getResponse, tt.err)
@ -204,6 +209,9 @@ func TestList(t *testing.T) {
},
true, nil, ""},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, []byte(tt.APIKey))
errTag := fmt.Sprintf("Test case #%d", i)
listRequest := pb.ListRequest{
@ -213,13 +221,12 @@ func TestList(t *testing.T) {
Recursive: tt.recursive,
Limit: int32(tt.limit),
MetaFlags: tt.metaFlags,
APIKey: []byte(tt.APIKey),
}
listResponse := pb.ListResponse{Items: tt.items, More: tt.more}
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{grpcClient: gc, APIKey: []byte(tt.APIKey)}
pdb := PointerDB{grpcClient: gc}
gc.EXPECT().List(gomock.Any(), &listRequest).Return(&listResponse, tt.err)
@ -265,11 +272,14 @@ func TestDelete(t *testing.T) {
{[]byte(""), p.New(""), ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), p.New("file1/file2"), nil, ""},
} {
deleteRequest := pb.DeleteRequest{Path: tt.path.String(), APIKey: tt.APIKey}
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
deleteRequest := pb.DeleteRequest{Path: tt.path.String()}
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{grpcClient: gc, APIKey: tt.APIKey}
pdb := PointerDB{grpcClient: gc}
gc.EXPECT().Delete(gomock.Any(), &deleteRequest).Return(nil, tt.err)

View File

@ -14,9 +14,10 @@ import (
"google.golang.org/grpc/status"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/auth"
"storj.io/storj/pkg/overlay"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/pointerdb/auth"
pointerdbAuth "storj.io/storj/pkg/pointerdb/auth"
"storj.io/storj/pkg/storage/meta"
"storj.io/storj/storage"
)
@ -44,8 +45,9 @@ func NewServer(db storage.KeyValueStore, cache *overlay.Cache, logger *zap.Logge
}
}
func (s *Server) validateAuth(APIKey []byte) error {
if !auth.ValidateAPIKey(string(APIKey)) {
func (s *Server) validateAuth(ctx context.Context) error {
APIKey, ok := auth.GetAPIKey(ctx)
if !ok || !pointerdbAuth.ValidateAPIKey(string(APIKey)) {
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, "Invalid API credential")))
return status.Errorf(codes.Unauthenticated, "Invalid API credential")
}
@ -80,7 +82,7 @@ func (s *Server) Put(ctx context.Context, req *pb.PutRequest) (resp *pb.PutRespo
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
if err = s.validateAuth(req.GetAPIKey()); err != nil {
if err = s.validateAuth(ctx); err != nil {
return nil, err
}
@ -110,7 +112,7 @@ func (s *Server) Get(ctx context.Context, req *pb.GetRequest) (resp *pb.GetRespo
s.logger.Debug("entering pointerdb get")
if err = s.validateAuth(req.GetAPIKey()); err != nil {
if err = s.validateAuth(ctx); err != nil {
return nil, err
}
@ -160,7 +162,7 @@ func (s *Server) Get(ctx context.Context, req *pb.GetRequest) (resp *pb.GetRespo
func (s *Server) List(ctx context.Context, req *pb.ListRequest) (resp *pb.ListResponse, err error) {
defer mon.Task()(&ctx)(&err)
if err = s.validateAuth(req.APIKey); err != nil {
if err = s.validateAuth(ctx); err != nil {
return nil, err
}
@ -247,7 +249,7 @@ func (s *Server) Delete(ctx context.Context, req *pb.DeleteRequest) (resp *pb.De
defer mon.Task()(&ctx)(&err)
s.logger.Debug("entering pointerdb delete")
if err = s.validateAuth(req.GetAPIKey()); err != nil {
if err = s.validateAuth(ctx); err != nil {
return nil, err
}

View File

@ -21,12 +21,10 @@ import (
"storj.io/storj/pkg/paths"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storage/meta"
"storj.io/storj/pkg/auth"
"storj.io/storj/storage"
"storj.io/storj/storage/teststore"
)
var (
ctx = context.Background()
)
func TestServicePut(t *testing.T) {
@ -39,6 +37,9 @@ func TestServicePut(t *testing.T) {
{[]byte("wrong key"), nil, status.Errorf(codes.Unauthenticated, "Invalid API credential").Error()},
{nil, errors.New("put error"), status.Errorf(codes.Internal, "internal error").Error()},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.apiKey)
errTag := fmt.Sprintf("Test case #%d", i)
db := teststore.New()
@ -51,7 +52,7 @@ func TestServicePut(t *testing.T) {
db.ForceError++
}
req := pb.PutRequest{Path: path, Pointer: &pr, APIKey: tt.apiKey}
req := pb.PutRequest{Path: path, Pointer: &pr}
_, err := s.Put(ctx, &req)
if err != nil {
@ -72,6 +73,9 @@ func TestServiceGet(t *testing.T) {
{[]byte("wrong key"), nil, status.Errorf(codes.Unauthenticated, "Invalid API credential").Error()},
{nil, errors.New("get error"), status.Errorf(codes.Internal, "internal error").Error()},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.apiKey)
errTag := fmt.Sprintf("Test case #%d", i)
db := teststore.New()
@ -89,7 +93,7 @@ func TestServiceGet(t *testing.T) {
db.ForceError++
}
req := pb.GetRequest{Path: path, APIKey: tt.apiKey}
req := pb.GetRequest{Path: path}
resp, err := s.Get(ctx, &req)
if err != nil {
@ -112,6 +116,9 @@ func TestServiceDelete(t *testing.T) {
{[]byte("wrong key"), nil, status.Errorf(codes.Unauthenticated, "Invalid API credential").Error()},
{nil, errors.New("delete error"), status.Errorf(codes.Internal, "internal error").Error()},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.apiKey)
errTag := fmt.Sprintf("Test case #%d", i)
path := "a/b/c"
@ -124,7 +131,7 @@ func TestServiceDelete(t *testing.T) {
db.ForceError++
}
req := pb.DeleteRequest{Path: path, APIKey: tt.apiKey}
req := pb.DeleteRequest{Path: path}
_, err := s.Delete(ctx, &req)
if err != nil {
@ -166,6 +173,7 @@ func TestServiceList(t *testing.T) {
}
type Test struct {
APIKey string
Request pb.ListRequest
Expected *pb.ListResponse
Error func(i int, err error)
@ -209,7 +217,8 @@ func TestServiceList(t *testing.T) {
},
},
}, {
Request: pb.ListRequest{Recursive: true, MetaFlags: meta.All, APIKey: []byte("wrong key")},
APIKey: "wrong key",
Request: pb.ListRequest{Recursive: true, MetaFlags: meta.All},//, APIKey: []byte("wrong key")},
Error: errorWithCode(codes.Unauthenticated),
}, {
Request: pb.ListRequest{Recursive: true, Limit: 3},
@ -303,6 +312,9 @@ func TestServiceList(t *testing.T) {
// pb.ListRequest{Prefix: "müsic/", StartAfter: "söng1.mp3", EndBefore: "söng4.mp3"},
// failing database
for i, test := range tests {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, []byte(test.APIKey))
resp, err := server.List(ctx, &test.Request)
if test.Error == nil {
if err != nil {

View File

@ -218,7 +218,7 @@ func (ic IdentityConfig) Save(fi *FullIdentity) error {
}
// Run will run the given responsibilities with the configured identity.
func (ic IdentityConfig) Run(ctx context.Context,
func (ic IdentityConfig) Run(ctx context.Context, interceptor grpc.UnaryServerInterceptor,
responsibilities ...Responsibility) (
err error) {
defer mon.Task()(&ctx)(&err)
@ -234,7 +234,7 @@ func (ic IdentityConfig) Run(ctx context.Context,
}
defer func() { _ = lis.Close() }()
s, err := NewProvider(pi, lis, responsibilities...)
s, err := NewProvider(pi, lis, interceptor, responsibilities...)
if err != nil {
return err
}

View File

@ -39,9 +39,9 @@ type Provider struct {
identity *FullIdentity
}
// NewProvider creates a Provider out of an Identity, a net.Listener, and a set
// of responsibilities.
func NewProvider(identity *FullIdentity, lis net.Listener,
// NewProvider creates a Provider out of an Identity, a net.Listener, a UnaryInterceptorProvider and
// a set of responsibilities.
func NewProvider(identity *FullIdentity, lis net.Listener, interceptor grpc.UnaryServerInterceptor,
responsibilities ...Responsibility) (*Provider, error) {
// NB: talk to anyone with an identity
ident, err := identity.ServerOption()
@ -49,6 +49,11 @@ func NewProvider(identity *FullIdentity, lis net.Listener,
return nil, err
}
unaryInterceptor := unaryInterceptor
if interceptor != nil {
unaryInterceptor = combineInterceptors(unaryInterceptor, interceptor)
}
return &Provider{
lis: lis,
g: grpc.NewServer(
@ -145,3 +150,13 @@ func unaryInterceptor(ctx context.Context, req interface{},
}
return resp, err
}
func combineInterceptors(a, b grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
return a(ctx, req, info, func(actx context.Context, areq interface{}) (interface{}, error) {
return b(actx, areq, info, func(bctx context.Context, breq interface{}) (interface{}, error) {
return handler(bctx, breq)
})
})
}
}