// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package logical import ( "context" "errors" "strings" ) type StorageView struct { storage Storage prefix string } var ErrRelativePath = errors.New("relative paths not supported") func NewStorageView(storage Storage, prefix string) *StorageView { return &StorageView{ storage: storage, prefix: prefix, } } // logical.Storage impl. func (s *StorageView) List(ctx context.Context, prefix string) ([]string, error) { if err := s.SanityCheck(prefix); err != nil { return nil, err } return s.storage.List(ctx, s.ExpandKey(prefix)) } // logical.Storage impl. func (s *StorageView) Get(ctx context.Context, key string) (*StorageEntry, error) { if err := s.SanityCheck(key); err != nil { return nil, err } entry, err := s.storage.Get(ctx, s.ExpandKey(key)) if err != nil { return nil, err } if entry == nil { return nil, nil } entry.Key = s.TruncateKey(entry.Key) return &StorageEntry{ Key: entry.Key, Value: entry.Value, SealWrap: entry.SealWrap, }, nil } // logical.Storage impl. func (s *StorageView) Put(ctx context.Context, entry *StorageEntry) error { if entry == nil { return errors.New("cannot write nil entry") } if err := s.SanityCheck(entry.Key); err != nil { return err } expandedKey := s.ExpandKey(entry.Key) nested := &StorageEntry{ Key: expandedKey, Value: entry.Value, SealWrap: entry.SealWrap, } return s.storage.Put(ctx, nested) } // logical.Storage impl. func (s *StorageView) Delete(ctx context.Context, key string) error { if err := s.SanityCheck(key); err != nil { return err } expandedKey := s.ExpandKey(key) return s.storage.Delete(ctx, expandedKey) } func (s *StorageView) Prefix() string { return s.prefix } // SubView constructs a nested sub-view using the given prefix func (s *StorageView) SubView(prefix string) *StorageView { sub := s.ExpandKey(prefix) return &StorageView{storage: s.storage, prefix: sub} } // SanityCheck is used to perform a sanity check on a key func (s *StorageView) SanityCheck(key string) error { if strings.Contains(key, "..") { return ErrRelativePath } return nil } // ExpandKey is used to expand to the full key path with the prefix func (s *StorageView) ExpandKey(suffix string) string { return s.prefix + suffix } // TruncateKey is used to remove the prefix of the key func (s *StorageView) TruncateKey(full string) string { return strings.TrimPrefix(full, s.prefix) }