c7f9d185b0
* Update kv command to use a preflight check * Make the existing ui endpoint return the allowed mounts * Add kv subcommand tests * Enable `-field` in `vault kv get/put` (#4426) * Enable `-field` in `vault kv get/put` Fixes #4424 * Unify nil value handling * Use preflight helper * Update vkv plugin * Add all the mount info when authenticated * Add fix the error message on put * add metadata test * No need to sort the capabilities * Remove the kv client header * kv patch command (#4432) * Fix test * Fix tests * Use permission denied instead of entity disabled
129 lines
2.9 KiB
Go
129 lines
2.9 KiB
Go
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/hashicorp/vault/helper/strutil"
|
|
)
|
|
|
|
func kvReadRequest(client *api.Client, path string, params map[string]string) (*api.Secret, error) {
|
|
r := client.NewRequest("GET", "/v1/"+path)
|
|
for k, v := range params {
|
|
r.Params.Set(k, v)
|
|
}
|
|
resp, err := client.RawRequest(r)
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
}
|
|
if resp != nil && resp.StatusCode == 404 {
|
|
secret, parseErr := api.ParseSecret(resp.Body)
|
|
switch parseErr {
|
|
case nil:
|
|
case io.EOF:
|
|
return nil, nil
|
|
default:
|
|
return nil, err
|
|
}
|
|
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
|
return secret, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return api.ParseSecret(resp.Body)
|
|
}
|
|
|
|
func kvPreflightVersionRequest(client *api.Client, path string) (string, int, error) {
|
|
r := client.NewRequest("GET", "/v1/sys/internal/ui/mount/"+path)
|
|
resp, err := client.RawRequest(r)
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
}
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
|
|
secret, err := api.ParseSecret(resp.Body)
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
var mountPath string
|
|
if mountPathRaw, ok := secret.Data["path"]; ok {
|
|
mountPath = mountPathRaw.(string)
|
|
}
|
|
options := secret.Data["options"]
|
|
if options == nil {
|
|
return mountPath, 1, nil
|
|
}
|
|
versionRaw := options.(map[string]interface{})["version"]
|
|
if versionRaw == nil {
|
|
return mountPath, 1, nil
|
|
}
|
|
version := versionRaw.(string)
|
|
switch version {
|
|
case "", "1":
|
|
return mountPath, 1, nil
|
|
case "2":
|
|
return mountPath, 2, nil
|
|
}
|
|
|
|
return mountPath, 1, nil
|
|
}
|
|
|
|
func isKVv2(path string, client *api.Client) (string, bool, error) {
|
|
mountPath, version, err := kvPreflightVersionRequest(client, path)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
return mountPath, version == 2, nil
|
|
}
|
|
|
|
func addPrefixToVKVPath(p, mountPath, apiPrefix string) string {
|
|
p = strings.TrimPrefix(p, mountPath)
|
|
return path.Join(mountPath, apiPrefix, p)
|
|
}
|
|
|
|
func getHeaderForMap(header string, data map[string]interface{}) string {
|
|
maxKey := 0
|
|
for k := range data {
|
|
if len(k) > maxKey {
|
|
maxKey = len(k)
|
|
}
|
|
}
|
|
|
|
// 4 for the column spaces and 5 for the len("value")
|
|
totalLen := maxKey + 4 + 5
|
|
|
|
equalSigns := totalLen - (len(header) + 2)
|
|
|
|
// If we have zero or fewer equal signs bump it back up to two on either
|
|
// side of the header.
|
|
if equalSigns <= 0 {
|
|
equalSigns = 4
|
|
}
|
|
|
|
// If the number of equal signs is not divisible by two add a sign.
|
|
if equalSigns%2 != 0 {
|
|
equalSigns = equalSigns + 1
|
|
}
|
|
|
|
return fmt.Sprintf("%s %s %s", strings.Repeat("=", equalSigns/2), header, strings.Repeat("=", equalSigns/2))
|
|
}
|
|
|
|
func kvParseVersionsFlags(versions []string) []string {
|
|
versionsOut := make([]string, 0, len(versions))
|
|
for _, v := range versions {
|
|
versionsOut = append(versionsOut, strutil.ParseStringSlice(v, ",")...)
|
|
}
|
|
|
|
return versionsOut
|
|
}
|