open-vault/command/base_helpers.go

226 lines
5.4 KiB
Go
Raw Normal View History

package command
import (
"fmt"
2017-09-05 03:54:46 +00:00
"io"
"strings"
2017-09-05 03:54:46 +00:00
"time"
"github.com/hashicorp/vault/api"
kvbuilder "github.com/hashicorp/vault/helper/kv-builder"
homedir "github.com/mitchellh/go-homedir"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/ryanuber/columnize"
)
2017-09-05 03:54:46 +00:00
var ErrMissingID = fmt.Errorf("Missing ID!")
var ErrMissingPath = fmt.Errorf("Missing PATH!")
2017-09-05 03:54:46 +00:00
var ErrMissingThing = fmt.Errorf("Missing THING!")
// extractListData reads the secret and returns a typed list of data and a
// boolean indicating whether the extraction was successful.
func extractListData(secret *api.Secret) ([]interface{}, bool) {
if secret == nil || secret.Data == nil {
return nil, false
}
k, ok := secret.Data["keys"]
if !ok || k == nil {
return nil, false
}
i, ok := k.([]interface{})
return i, ok
}
// extractPath extracts the path and list of arguments from the args. If there
// are no extra arguments, the remaining args will be nil.
func extractPath(args []string) (string, []string, error) {
2017-09-05 03:54:46 +00:00
str, remaining, err := extractThings(args)
if err == ErrMissingThing {
err = ErrMissingPath
}
return str, remaining, err
}
// extractID extracts the path and list of arguments from the args. If there
// are no extra arguments, the remaining args will be nil.
func extractID(args []string) (string, []string, error) {
str, remaining, err := extractThings(args)
if err == ErrMissingThing {
err = ErrMissingID
}
return str, remaining, err
}
func extractThings(args []string) (string, []string, error) {
if len(args) < 1 {
2017-09-05 03:54:46 +00:00
return "", nil, ErrMissingThing
}
// Path is always the first argument after all flags
2017-09-05 03:54:46 +00:00
thing := args[0]
// Strip leading and trailing slashes
2017-09-05 03:54:46 +00:00
thing = sanitizePath(thing)
2017-09-05 03:54:46 +00:00
// Verify we have a thing
if thing == "" {
return "", nil, ErrMissingThing
}
// Splice remaining args
var remaining []string
if len(args) > 1 {
remaining = args[1:]
}
2017-09-05 03:54:46 +00:00
return thing, remaining, nil
}
// sanitizePath removes any leading or trailing things from a "path".
func sanitizePath(s string) string {
return ensureNoTrailingSlash(ensureNoLeadingSlash(s))
}
// ensureTrailingSlash ensures the given string has a trailing slash.
func ensureTrailingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[len(s)-1] != '/' {
s = s + "/"
}
return s
}
// ensureNoTrailingSlash ensures the given string has a trailing slash.
func ensureNoTrailingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[len(s)-1] == '/' {
s = s[:len(s)-1]
}
return s
}
// ensureNoLeadingSlash ensures the given string has a trailing slash.
func ensureNoLeadingSlash(s string) string {
s = strings.TrimSpace(s)
if s == "" {
return ""
}
for len(s) > 0 && s[0] == '/' {
s = s[1:]
}
return s
}
// columnOuput prints the list of items as a table with no headers.
func columnOutput(list []string) string {
if len(list) == 0 {
return ""
}
return columnize.Format(list, &columnize.Config{
Glue: " ",
Empty: "n/a",
})
}
// tableOutput prints the list of items as columns, where the first row is
// the list of headers.
func tableOutput(list []string) string {
if len(list) == 0 {
return ""
}
underline := ""
headers := strings.Split(list[0], "|")
for i, h := range headers {
h = strings.TrimSpace(h)
u := strings.Repeat("-", len(h))
underline = underline + u
if i != len(headers)-1 {
underline = underline + " | "
}
}
list = append(list, "")
copy(list[2:], list[1:])
list[1] = underline
return columnOutput(list)
}
// parseArgsData parses the given args in the format key=value into a map of
// the provided arguments. The given reader can also supply key=value pairs.
func parseArgsData(stdin io.Reader, args []string) (map[string]interface{}, error) {
builder := &kvbuilder.Builder{Stdin: stdin}
if err := builder.Add(args...); err != nil {
return nil, err
}
return builder.Map(), nil
}
// parseArgsDataString parses the args data and returns the values as strings.
// If the values cannot be represented as strings, an error is returned.
func parseArgsDataString(stdin io.Reader, args []string) (map[string]string, error) {
raw, err := parseArgsData(stdin, args)
if err != nil {
return nil, err
}
var result map[string]string
if err := mapstructure.WeakDecode(raw, &result); err != nil {
return nil, errors.Wrap(err, "failed to convert values to strings")
}
return result, nil
}
// truncateToSeconds truncates the given duaration to the number of seconds. If
// the duration is less than 1s, it is returned as 0. The integer represents
// the whole number unit of seconds for the duration.
func truncateToSeconds(d time.Duration) int {
d = d.Truncate(1 * time.Second)
// Handle the case where someone requested a ridiculously short increment -
// incremenents must be larger than a second.
if d < 1*time.Second {
return 0
}
return int(d.Seconds())
}
// printKeyStatus prints the KeyStatus response from the API.
func printKeyStatus(ks *api.KeyStatus) string {
return columnOutput([]string{
fmt.Sprintf("Key Term | %d", ks.Term),
fmt.Sprintf("Install Time | %s", ks.InstallTime.UTC().Format(time.RFC822)),
})
}
// expandPath takes a filepath and returns the full expanded path, accounting
// for user-relative things like ~/.
func expandPath(s string) string {
if s == "" {
return ""
}
e, err := homedir.Expand(s)
if err != nil {
return s
}
return e
}