| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package physical |
| |
| import ( |
| "context" |
| "errors" |
| "strings" |
| "unicode" |
| "unicode/utf8" |
| ) |
| |
| var ( |
| ErrNonUTF8 = errors.New("key contains invalid UTF-8 characters") |
| ErrNonPrintable = errors.New("key contains non-printable characters") |
| ) |
| |
| // StorageEncoding is used to add errors into underlying physical requests |
| type StorageEncoding struct { |
| Backend |
| } |
| |
| // TransactionalStorageEncoding is the transactional version of the error |
| // injector |
| type TransactionalStorageEncoding struct { |
| *StorageEncoding |
| Transactional |
| } |
| |
| // Verify StorageEncoding satisfies the correct interfaces |
| var ( |
| _ Backend = (*StorageEncoding)(nil) |
| _ Transactional = (*TransactionalStorageEncoding)(nil) |
| ) |
| |
| // NewStorageEncoding returns a wrapped physical backend and verifies the key |
| // encoding |
| func NewStorageEncoding(b Backend) Backend { |
| enc := &StorageEncoding{ |
| Backend: b, |
| } |
| |
| if bTxn, ok := b.(Transactional); ok { |
| return &TransactionalStorageEncoding{ |
| StorageEncoding: enc, |
| Transactional: bTxn, |
| } |
| } |
| |
| return enc |
| } |
| |
| func (e *StorageEncoding) containsNonPrintableChars(key string) bool { |
| idx := strings.IndexFunc(key, func(c rune) bool { |
| return !unicode.IsPrint(c) |
| }) |
| |
| return idx != -1 |
| } |
| |
| func (e *StorageEncoding) Put(ctx context.Context, entry *Entry) error { |
| if !utf8.ValidString(entry.Key) { |
| return ErrNonUTF8 |
| } |
| |
| if e.containsNonPrintableChars(entry.Key) { |
| return ErrNonPrintable |
| } |
| |
| return e.Backend.Put(ctx, entry) |
| } |
| |
| func (e *StorageEncoding) Delete(ctx context.Context, key string) error { |
| if !utf8.ValidString(key) { |
| return ErrNonUTF8 |
| } |
| |
| if e.containsNonPrintableChars(key) { |
| return ErrNonPrintable |
| } |
| |
| return e.Backend.Delete(ctx, key) |
| } |
| |
| func (e *TransactionalStorageEncoding) Transaction(ctx context.Context, txns []*TxnEntry) error { |
| for _, txn := range txns { |
| if !utf8.ValidString(txn.Entry.Key) { |
| return ErrNonUTF8 |
| } |
| |
| if e.containsNonPrintableChars(txn.Entry.Key) { |
| return ErrNonPrintable |
| } |
| |
| } |
| |
| return e.Transactional.Transaction(ctx, txns) |
| } |
| |
| func (e *StorageEncoding) Purge(ctx context.Context) { |
| if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok { |
| purgeable.Purge(ctx) |
| } |
| } |
| |
| func (e *StorageEncoding) SetEnabled(enabled bool) { |
| if purgeable, ok := e.Backend.(ToggleablePurgemonster); ok { |
| purgeable.SetEnabled(enabled) |
| } |
| } |