diff --git a/CHANGELOG.md b/CHANGELOG.md index a825b1d3..0ab3fb2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# Changelog + +## master (unreleased) + +ENHANCEMENTS: + +* Allow limiting scope of grep to keys or values ([#66](https://github.com/fishi0x01/vsh/pull/66) - Thank you for implementation [mattlqx](https://github.com/mattlqx)) +* Do not show and operate on KV2 metadata ([#68](https://github.com/fishi0x01/vsh/pull/68) + ## v0.8.0 (January 27, 2021) ENHANCEMENTS: diff --git a/README.md b/README.md index f98e30ff..a9493004 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Core features are: ## Installation -### MacOS / Brew +### Homebrew ```sh brew install vsh @@ -32,13 +32,7 @@ nix-env -i vsh ### Static binaries for Linux / MacOS -E.g., install to `/usr/local/bin`: - -```sh -sudo curl -sLo /usr/local/bin/vsh https://github.com/fishi0x01/vsh/releases/download/v0.8.0/vsh_$(uname | tr '[:upper:]' '[:lower:]')_amd64 -``` - -It is recommendable to verify [checksums](https://github.com/fishi0x01/vsh/releases/download/v0.8.0/SHA256SUM) for data integrity. +Download latest static binaries from [release page](https://github.com/fishi0x01/vsh/releases). ## Supported commands diff --git a/cli/append.go b/cli/append.go index 29bff567..a1d54207 100644 --- a/cli/append.go +++ b/cli/append.go @@ -139,8 +139,9 @@ func (cmd *AppendCommand) createDummySecret(target string) error { dummy := make(map[string]interface{}) dummy["placeholder"] = struct{}{} + dummySecret := client.NewSecret(&api.Secret{Data: dummy}) if targetSecret == nil { - if err = cmd.client.Write(target, &api.Secret{Data: dummy}); err != nil { + if err = cmd.client.Write(target, dummySecret); err != nil { return err } } @@ -159,32 +160,17 @@ func (cmd *AppendCommand) mergeSecrets(source string, target string) error { } onConflict := cmd.Mode - merged := make(map[string]interface{}) + merged := targetSecret.GetData() skippedKeys := make([]string, 0) - for k, v := range targetSecret.Data { - if rec, ok := v.(map[string]interface{}); ok { - for kk, vv := range rec { - merged[kk] = vv - } - } else { - merged[k] = v - } + for k, v := range sourceSecret.GetData() { + skipped := addKey(merged, onConflict, k, v) + skippedKeys = append(skippedKeys, skipped...) } - for k, v := range sourceSecret.Data { - if rec, ok := v.(map[string]interface{}); ok { - for kk, vv := range rec { - skipped := addKey(merged, onConflict, kk, vv) - skippedKeys = append(skippedKeys, skipped...) - } - } else { - skipped := addKey(merged, onConflict, k, v) - skippedKeys = append(skippedKeys, skipped...) - } - } // write - if err := cmd.client.Write(target, &api.Secret{Data: merged}); err != nil { + resultSecret := client.NewSecret(&api.Secret{Data: merged}) + if err := cmd.client.Write(target, resultSecret); err != nil { fmt.Println(err) return err } diff --git a/cli/cat.go b/cli/cat.go index e9df2f07..4e0576a4 100644 --- a/cli/cat.go +++ b/cli/cat.go @@ -57,16 +57,8 @@ func (cmd *CatCommand) Run() int { return 1 } - for k, v := range secret.Data { - if rec, ok := v.(map[string]interface{}); ok { - // KV 2 - for kk, vv := range rec { - log.UserInfo("%s = %s", kk, vv) - } - } else { - // KV 1 - log.UserInfo("%s = %s", k, v) - } + for k, v := range secret.GetData() { + log.UserInfo("%s = %s", k, v) } } else { log.UserError("Not a valid path for operation: %s", absPath) diff --git a/cli/grep.go b/cli/grep.go index 55a62430..8dad0409 100644 --- a/cli/grep.go +++ b/cli/grep.go @@ -147,16 +147,8 @@ func (cmd *GrepCommand) grepFile(search string, path string) (matches []*Match, return matches, err } - for k, v := range secret.Data { - if rec, ok := v.(map[string]interface{}); ok { - // KV 2 - for kk, vv := range rec { - matches = append(matches, cmd.doMatch(path, kk, fmt.Sprintf("%v", vv), search)...) - } - } else { - // KV 1 - matches = append(matches, cmd.doMatch(path, k, fmt.Sprintf("%v", v), search)...) - } + for k, v := range secret.GetData() { + matches = append(matches, cmd.doMatch(path, k, fmt.Sprintf("%v", v), search)...) } } diff --git a/client/client.go b/client/client.go index b1983305..a2abd218 100644 --- a/client/client.go +++ b/client/client.go @@ -95,22 +95,25 @@ func NewClient(conf *VaultConfig) (*Client, error) { } // Read returns secret at given path, using given Client -func (client *Client) Read(absolutePath string) (secret *api.Secret, err error) { +func (client *Client) Read(absolutePath string) (secret *Secret, err error) { + var apiSecret *api.Secret if client.isTopLevelPath(absolutePath) { - secret, err = client.topLevelRead(normalizedVaultPath(absolutePath)) + apiSecret, err = client.topLevelRead(normalizedVaultPath(absolutePath)) } else { - secret, err = client.lowLevelRead(normalizedVaultPath(absolutePath)) + apiSecret, err = client.lowLevelRead(normalizedVaultPath(absolutePath)) + } + if apiSecret != nil { + secret = NewSecret(apiSecret) } - return secret, err } // Write writes secret to given path, using given Client -func (client *Client) Write(absolutePath string, secret *api.Secret) (err error) { +func (client *Client) Write(absolutePath string, secret *Secret) (err error) { if client.isTopLevelPath(absolutePath) { err = client.topLevelWrite(normalizedVaultPath(absolutePath)) } else { - err = client.lowLevelWrite(normalizedVaultPath(absolutePath), secret) + err = client.lowLevelWrite(normalizedVaultPath(absolutePath), secret.GetAPISecret()) } return err diff --git a/client/secret.go b/client/secret.go new file mode 100644 index 00000000..561127b3 --- /dev/null +++ b/client/secret.go @@ -0,0 +1,56 @@ +package client + +import ( + "github.com/hashicorp/vault/api" +) + +// Secret holds vault secret and offers operations to simplify KV abstraction +type Secret struct { + vaultSecret *api.Secret +} + +// NewSecret create a new Secret object +func NewSecret(vaultSecret *api.Secret) *Secret { + return &Secret{ + vaultSecret: vaultSecret, + } +} + +// GetAPISecret getter method for vault secret in Secret object +func (secret *Secret) GetAPISecret() *api.Secret { + return secret.vaultSecret +} + +// GetData returns the secret data as a map and is KV agnostic +func (secret *Secret) GetData() map[string]interface{} { + data := make(map[string]interface{}) + for k, v := range secret.vaultSecret.Data { + if rec, ok := v.(map[string]interface{}); ok { + // KV 2 + if k == "data" { + for kk, vv := range rec { + data[kk] = vv + } + } + } else { + // KV 1 + data[k] = v + } + } + return data +} + +// SetData set given data as vault secret data and is KV agnostic +func (secret *Secret) SetData(data map[string]interface{}) { + isKV2 := false + if val, hasData := secret.vaultSecret.Data["data"]; hasData { + if _, isKV2 := val.(map[string]interface{}); isKV2 { + // KV2 + secret.vaultSecret.Data["data"] = data + } + } + if !isKV2 { + // KV1 + secret.vaultSecret.Data = data + } +} diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh index c54b6ee0..9eee2fde 100755 --- a/test/run-all-tests.sh +++ b/test/run-all-tests.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e # required to fail test suite when a single test fails -VAULT_VERSIONS=("1.6.1" "1.0.0") +VAULT_VERSIONS=("1.6.2" "1.0.0") KV_BACKENDS=("KV1" "KV2") DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" diff --git a/test/run-single-test.sh b/test/run-single-test.sh index c53adee7..ce20586f 100755 --- a/test/run-single-test.sh +++ b/test/run-single-test.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e # required to fail test suite when a single test fails -VAULT_VERSION=${VAULT_VERSION:-"1.6.1"} +VAULT_VERSION=${VAULT_VERSION:-"1.6.2"} KV_BACKEND=${KV_BACKEND:-"KV2"} TEST_SUITE=${TEST_SUITE:-"commands/cp"} diff --git a/test/suites/commands/cat.bats b/test/suites/commands/cat.bats index 70249140..49343996 100644 --- a/test/suites/commands/cat.bats +++ b/test/suites/commands/cat.bats @@ -8,6 +8,9 @@ load ../../bin/plugins/bats-assert/load run ${APP_BIN} -c "cat ${KV_BACKEND}/src/dev/1" assert_success assert_line "value = 1" + refute_line --partial "created_time" + refute_line --partial "deletion_time" + refute_line --partial "destroyed" ####################################### echo "==== case: cat non-existing file ====" @@ -30,6 +33,9 @@ load ../../bin/plugins/bats-assert/load assert_line "value = tooling" assert_line "drink = beer" assert_line "key = A" + refute_line --partial "created_time" + refute_line --partial "deletion_time" + refute_line --partial "destroyed" ####################################### echo "==== case: cat ambiguous directory ===="