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  }