github.com/hashicorp/vault/sdk@v0.13.0/physical/encoding.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package physical 5 6 import ( 7 "context" 8 "errors" 9 "strings" 10 "unicode" 11 "unicode/utf8" 12 ) 13 14 var ( 15 ErrNonUTF8 = errors.New("key contains invalid UTF-8 characters") 16 ErrNonPrintable = errors.New("key contains non-printable characters") 17 ) 18 19 // StorageEncoding is used to add errors into underlying physical requests 20 type StorageEncoding struct { 21 Backend 22 } 23 24 // TransactionalStorageEncoding is the transactional version of the error 25 // injector 26 type TransactionalStorageEncoding struct { 27 *StorageEncoding 28 Transactional 29 } 30 31 // Verify StorageEncoding satisfies the correct interfaces 32 var ( 33 _ Backend = (*StorageEncoding)(nil) 34 _ Transactional = (*TransactionalStorageEncoding)(nil) 35 ) 36 37 // NewStorageEncoding returns a wrapped physical backend and verifies the key 38 // encoding 39 func NewStorageEncoding(b Backend) Backend { 40 enc := &StorageEncoding{ 41 Backend: b, 42 } 43 44 if bTxn, ok := b.(Transactional); ok { 45 return &TransactionalStorageEncoding{ 46 StorageEncoding: enc, 47 Transactional: bTxn, 48 } 49 } 50 51 return enc 52 } 53 54 func (e *StorageEncoding) containsNonPrintableChars(key string) bool { 55 idx := strings.IndexFunc(key, func(c rune) bool { 56 return !unicode.IsPrint(c) 57 }) 58 59 return idx != -1 60 } 61 62 func (e *StorageEncoding) Put(ctx context.Context, entry *Entry) error { 63 if !utf8.ValidString(entry.Key) { 64 return ErrNonUTF8 65 } 66 67 if e.containsNonPrintableChars(entry.Key) { 68 return ErrNonPrintable 69 } 70 71 return e.Backend.Put(ctx, entry) 72 } 73 74 func (e *StorageEncoding) Delete(ctx context.Context, key string) error { 75 if !utf8.ValidString(key) { 76 return ErrNonUTF8 77 } 78 79 if e.containsNonPrintableChars(key) { 80 return ErrNonPrintable 81 } 82 83 return e.Backend.Delete(ctx, key) 84 } 85 86 func (e *TransactionalStorageEncoding) Transaction(ctx context.Context, txns []*TxnEntry) error { 87 for _, txn := range txns { 88 if !utf8.ValidString(txn.Entry.Key) { 89 return ErrNonUTF8 90 } 91 92 if e.containsNonPrintableChars(txn.Entry.Key) { 93 return ErrNonPrintable 94 } 95 96 } 97 98 return e.Transactional.Transaction(ctx, txns) 99 } 100 101 // TransactionLimits implements physical.TransactionalLimits 102 func (e *TransactionalStorageEncoding) TransactionLimits() (int, int) { 103 if tl, ok := e.Transactional.(TransactionalLimits); ok { 104 return tl.TransactionLimits() 105 } 106 // We don't have any specific limits of our own so return zeros to signal that 107 // the caller should use whatever reasonable defaults it would if it used a 108 // non-TransactionalLimits backend. 109 return 0, 0 110 } 111 112 func (e *StorageEncoding) Purge(ctx context.Context) { 113 if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok { 114 purgeable.Purge(ctx) 115 } 116 } 117 118 func (e *StorageEncoding) SetEnabled(enabled bool) { 119 if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok { 120 purgeable.SetEnabled(enabled) 121 } 122 }