2015-03-15 20:52:43 +00:00
|
|
|
package logical
|
|
|
|
|
2015-03-20 16:59:48 +00:00
|
|
|
import (
|
2017-01-07 23:18:22 +00:00
|
|
|
"errors"
|
2016-07-06 16:25:40 +00:00
|
|
|
"fmt"
|
2017-01-06 20:42:18 +00:00
|
|
|
"strings"
|
2016-07-06 16:25:40 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/vault/helper/jsonutil"
|
2015-03-20 16:59:48 +00:00
|
|
|
)
|
|
|
|
|
2017-01-07 23:18:22 +00:00
|
|
|
// ErrReadOnly is returned when a backend does not support
|
|
|
|
// writing. This can be caused by a read-only replica or secondary
|
|
|
|
// cluster operation.
|
|
|
|
var ErrReadOnly = errors.New("Cannot write to readonly storage")
|
|
|
|
|
2015-03-15 20:52:43 +00:00
|
|
|
// Storage is the way that logical backends are able read/write data.
|
|
|
|
type Storage interface {
|
|
|
|
List(prefix string) ([]string, error)
|
|
|
|
Get(string) (*StorageEntry, error)
|
|
|
|
Put(*StorageEntry) error
|
|
|
|
Delete(string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// StorageEntry is the entry for an item in a Storage implementation.
|
|
|
|
type StorageEntry struct {
|
|
|
|
Key string
|
|
|
|
Value []byte
|
|
|
|
}
|
2015-03-20 16:59:48 +00:00
|
|
|
|
2016-07-06 16:25:40 +00:00
|
|
|
// DecodeJSON decodes the 'Value' present in StorageEntry.
|
2015-03-20 16:59:48 +00:00
|
|
|
func (e *StorageEntry) DecodeJSON(out interface{}) error {
|
2016-07-06 16:25:40 +00:00
|
|
|
return jsonutil.DecodeJSON(e.Value, out)
|
2015-03-20 16:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StorageEntryJSON creates a StorageEntry with a JSON-encoded value.
|
|
|
|
func StorageEntryJSON(k string, v interface{}) (*StorageEntry, error) {
|
2016-07-06 16:25:40 +00:00
|
|
|
encodedBytes, err := jsonutil.EncodeJSON(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to encode storage entry: %v", err)
|
2015-03-20 16:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &StorageEntry{
|
|
|
|
Key: k,
|
2016-07-06 16:25:40 +00:00
|
|
|
Value: encodedBytes,
|
2015-03-20 16:59:48 +00:00
|
|
|
}, nil
|
|
|
|
}
|
2017-01-06 20:42:18 +00:00
|
|
|
|
|
|
|
type ClearableView interface {
|
|
|
|
List(string) ([]string, error)
|
|
|
|
Delete(string) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// ScanView is used to scan all the keys in a view iteratively
|
|
|
|
func ScanView(view ClearableView, cb func(path string)) error {
|
|
|
|
frontier := []string{""}
|
|
|
|
for len(frontier) > 0 {
|
|
|
|
n := len(frontier)
|
|
|
|
current := frontier[n-1]
|
|
|
|
frontier = frontier[:n-1]
|
|
|
|
|
|
|
|
// List the contents
|
|
|
|
contents, err := view.List(current)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("list failed at path '%s': %v", current, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle the contents in the directory
|
|
|
|
for _, c := range contents {
|
|
|
|
fullPath := current + c
|
|
|
|
if strings.HasSuffix(c, "/") {
|
|
|
|
frontier = append(frontier, fullPath)
|
|
|
|
} else {
|
|
|
|
cb(fullPath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CollectKeys is used to collect all the keys in a view
|
|
|
|
func CollectKeys(view ClearableView) ([]string, error) {
|
|
|
|
// Accumulate the keys
|
|
|
|
var existing []string
|
|
|
|
cb := func(path string) {
|
|
|
|
existing = append(existing, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan for all the keys
|
|
|
|
if err := ScanView(view, cb); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return existing, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClearView is used to delete all the keys in a view
|
|
|
|
func ClearView(view ClearableView) error {
|
|
|
|
// Collect all the keys
|
|
|
|
keys, err := CollectKeys(view)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete all the keys
|
|
|
|
for _, key := range keys {
|
|
|
|
if err := view.Delete(key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|