1package logical 2 3import ( 4 "context" 5 "errors" 6 "strings" 7) 8 9type StorageView struct { 10 storage Storage 11 prefix string 12} 13 14var ( 15 ErrRelativePath = errors.New("relative paths not supported") 16) 17 18func NewStorageView(storage Storage, prefix string) *StorageView { 19 return &StorageView{ 20 storage: storage, 21 prefix: prefix, 22 } 23} 24 25// logical.Storage impl. 26func (s *StorageView) List(ctx context.Context, prefix string) ([]string, error) { 27 if err := s.SanityCheck(prefix); err != nil { 28 return nil, err 29 } 30 return s.storage.List(ctx, s.ExpandKey(prefix)) 31} 32 33// logical.Storage impl. 34func (s *StorageView) Get(ctx context.Context, key string) (*StorageEntry, error) { 35 if err := s.SanityCheck(key); err != nil { 36 return nil, err 37 } 38 entry, err := s.storage.Get(ctx, s.ExpandKey(key)) 39 if err != nil { 40 return nil, err 41 } 42 if entry == nil { 43 return nil, nil 44 } 45 if entry != nil { 46 entry.Key = s.TruncateKey(entry.Key) 47 } 48 49 return &StorageEntry{ 50 Key: entry.Key, 51 Value: entry.Value, 52 SealWrap: entry.SealWrap, 53 }, nil 54} 55 56// logical.Storage impl. 57func (s *StorageView) Put(ctx context.Context, entry *StorageEntry) error { 58 if entry == nil { 59 return errors.New("cannot write nil entry") 60 } 61 62 if err := s.SanityCheck(entry.Key); err != nil { 63 return err 64 } 65 66 expandedKey := s.ExpandKey(entry.Key) 67 68 nested := &StorageEntry{ 69 Key: expandedKey, 70 Value: entry.Value, 71 SealWrap: entry.SealWrap, 72 } 73 74 return s.storage.Put(ctx, nested) 75} 76 77// logical.Storage impl. 78func (s *StorageView) Delete(ctx context.Context, key string) error { 79 if err := s.SanityCheck(key); err != nil { 80 return err 81 } 82 83 expandedKey := s.ExpandKey(key) 84 85 return s.storage.Delete(ctx, expandedKey) 86} 87 88func (s *StorageView) Prefix() string { 89 return s.prefix 90} 91 92// SubView constructs a nested sub-view using the given prefix 93func (s *StorageView) SubView(prefix string) *StorageView { 94 sub := s.ExpandKey(prefix) 95 return &StorageView{storage: s.storage, prefix: sub} 96} 97 98// SanityCheck is used to perform a sanity check on a key 99func (s *StorageView) SanityCheck(key string) error { 100 if strings.Contains(key, "..") { 101 return ErrRelativePath 102 } 103 return nil 104} 105 106// ExpandKey is used to expand to the full key path with the prefix 107func (s *StorageView) ExpandKey(suffix string) string { 108 return s.prefix + suffix 109} 110 111// TruncateKey is used to remove the prefix of the key 112func (s *StorageView) TruncateKey(full string) string { 113 return strings.TrimPrefix(full, s.prefix) 114} 115