From 9ed00822ea5404c63faa87560f222223d775a8ca Mon Sep 17 00:00:00 2001 From: Tom Proctor Date: Mon, 4 Mar 2024 18:29:20 +0000 Subject: [PATCH] Move CLI token helper to api module (#25744) * Move command/config + command/token to api/cliconfig + api/tokenhelper * Remove unused functions and unused import * Simplify and inline function copied from SDK * Delete unused duplicated/forwarding config implementation from command package * Delete unused code, unexport API surface that's only used internally to the package * Fix up license headers * Add changelog * Tweak .gitignore to track hcl files in testdata/ folders --- .gitignore | 1 + {command/config => api/cliconfig}/config.go | 70 ++++++++-------- {command => api/cliconfig}/config_test.go | 14 ++-- .../cliconfig/testdata}/config.hcl | 2 +- api/cliconfig/util.go | 28 +++++++ api/go.mod | 9 ++- api/go.sum | 18 +++-- {command/token => api/tokenhelper}/helper.go | 4 +- .../tokenhelper}/helper_external.go | 10 +-- .../tokenhelper}/helper_external_test.go | 11 ++- .../tokenhelper}/helper_internal.go | 4 +- .../tokenhelper}/helper_internal_test.go | 9 +-- api/tokenhelper/testing.go | 38 +++++++++ changelog/25744.txt | 3 + command/base.go | 11 +-- command/base_predict.go | 3 +- command/config.go | 57 ------------- command/config/config_test.go | 52 ------------ command/config/util.go | 28 ------- command/main.go | 4 +- command/token/helper_testing.go | 4 +- command/token/testing.go | 81 ------------------- command/util.go | 8 +- go.mod | 2 +- go.sum | 4 +- 25 files changed, 164 insertions(+), 311 deletions(-) rename {command/config => api/cliconfig}/config.go (56%) rename {command => api/cliconfig}/config_test.go (72%) rename {command/test-fixtures => api/cliconfig/testdata}/config.hcl (60%) create mode 100644 api/cliconfig/util.go rename {command/token => api/tokenhelper}/helper.go (88%) rename {command/token => api/tokenhelper}/helper_external.go (94%) rename {command/token => api/tokenhelper}/helper_external_test.go (92%) rename {command/token => api/tokenhelper}/helper_internal.go (97%) rename {command/token => api/tokenhelper}/helper_internal_test.go (89%) create mode 100644 api/tokenhelper/testing.go create mode 100644 changelog/25744.txt delete mode 100644 command/config.go delete mode 100644 command/config/config_test.go delete mode 100644 command/config/util.go delete mode 100644 command/token/testing.go diff --git a/.gitignore b/.gitignore index 95e675edf5..1675763341 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ Vagrantfile !command/agent/config/test-fixtures/*.hcl !command/server/test-fixtures/**/*.hcl !enos/**/*.hcl +!**/testdata/*.hcl # Enos .enos diff --git a/command/config/config.go b/api/cliconfig/config.go similarity index 56% rename from command/config/config.go rename to api/cliconfig/config.go index 4421226f24..f918f24395 100644 --- a/command/config/config.go +++ b/api/cliconfig/config.go @@ -1,31 +1,30 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package config +package cliconfig import ( "fmt" - "io/ioutil" "os" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/vault/sdk/helper/hclutil" - homedir "github.com/mitchellh/go-homedir" + "github.com/mitchellh/go-homedir" ) const ( - // DefaultConfigPath is the default path to the configuration file - DefaultConfigPath = "~/.vault" + // defaultConfigPath is the default path to the configuration file + defaultConfigPath = "~/.vault" - // ConfigPathEnv is the environment variable that can be used to + // configPathEnv is the environment variable that can be used to // override where the Vault configuration is. - ConfigPathEnv = "VAULT_CONFIG_PATH" + configPathEnv = "VAULT_CONFIG_PATH" ) // Config is the CLI configuration for Vault that can be specified via // a `$HOME/.vault` file which is HCL-formatted (therefore HCL or JSON). -type DefaultConfig struct { +type defaultConfig struct { // TokenHelper is the executable/command that is executed for storing // and retrieving the authentication token for the Vault CLI. If this // is not specified, then vault's internal token store will be used, which @@ -33,26 +32,14 @@ type DefaultConfig struct { TokenHelper string `hcl:"token_helper"` } -// Config loads the configuration and returns it. If the configuration -// is already loaded, it is returned. -func Config() (*DefaultConfig, error) { - var err error - config, err := LoadConfig("") - if err != nil { - return nil, err - } - - return config, nil -} - -// LoadConfig reads the configuration from the given path. If path is +// loadConfig reads the configuration from the given path. If path is // empty, then the default path will be used, or the environment variable // if set. -func LoadConfig(path string) (*DefaultConfig, error) { +func loadConfig(path string) (*defaultConfig, error) { if path == "" { - path = DefaultConfigPath + path = defaultConfigPath } - if v := os.Getenv(ConfigPathEnv); v != "" { + if v := os.Getenv(configPathEnv); v != "" { path = v } @@ -62,21 +49,21 @@ func LoadConfig(path string) (*DefaultConfig, error) { return nil, fmt.Errorf("error expanding config path %q: %w", path, err) } - contents, err := ioutil.ReadFile(path) + contents, err := os.ReadFile(path) if err != nil && !os.IsNotExist(err) { return nil, err } - conf, err := ParseConfig(string(contents)) + conf, err := parseConfig(string(contents)) if err != nil { - return nil, fmt.Errorf("error parsing config file at %q: %w; ensure that the file is valid; Ansible Vault is known to conflict with it.", path, err) + return nil, fmt.Errorf("error parsing config file at %q: %w; ensure that the file is valid; Ansible Vault is known to conflict with it", path, err) } return conf, nil } -// ParseConfig parses the given configuration as a string. -func ParseConfig(contents string) (*DefaultConfig, error) { +// parseConfig parses the given configuration as a string. +func parseConfig(contents string) (*defaultConfig, error) { root, err := hcl.Parse(contents) if err != nil { return nil, err @@ -88,14 +75,23 @@ func ParseConfig(contents string) (*DefaultConfig, error) { return nil, fmt.Errorf("failed to parse config; does not contain a root object") } - valid := []string{ - "token_helper", - } - if err := hclutil.CheckHCLKeys(list, valid); err != nil { - return nil, err + valid := map[string]struct{}{ + "token_helper": {}, } - var c DefaultConfig + var validationErrors error + for _, item := range list.Items { + key := item.Keys[0].Token.Value().(string) + if _, ok := valid[key]; !ok { + validationErrors = multierror.Append(validationErrors, fmt.Errorf("invalid key %q on line %d", key, item.Assign.Line)) + } + } + + if validationErrors != nil { + return nil, validationErrors + } + + var c defaultConfig if err := hcl.DecodeObject(&c, list); err != nil { return nil, err } diff --git a/command/config_test.go b/api/cliconfig/config_test.go similarity index 72% rename from command/config_test.go rename to api/cliconfig/config_test.go index 187d4ce8b4..5e2dedeaeb 100644 --- a/command/config_test.go +++ b/api/cliconfig/config_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package command +package cliconfig import ( "path/filepath" @@ -10,15 +10,13 @@ import ( "testing" ) -const FixturePath = "./test-fixtures" - func TestLoadConfig(t *testing.T) { - config, err := LoadConfig(filepath.Join(FixturePath, "config.hcl")) + config, err := loadConfig(filepath.Join("testdata", "config.hcl")) if err != nil { t.Fatalf("err: %s", err) } - expected := &DefaultConfig{ + expected := &defaultConfig{ TokenHelper: "foo", } if !reflect.DeepEqual(expected, config) { @@ -27,7 +25,7 @@ func TestLoadConfig(t *testing.T) { } func TestLoadConfig_noExist(t *testing.T) { - config, err := LoadConfig("nope/not-once/.never") + config, err := loadConfig("nope/not-once/.never") if err != nil { t.Fatal(err) } @@ -38,7 +36,7 @@ func TestLoadConfig_noExist(t *testing.T) { } func TestParseConfig_badKeys(t *testing.T) { - _, err := ParseConfig(` + _, err := parseConfig(` token_helper = "/token" nope = "true" `) diff --git a/command/test-fixtures/config.hcl b/api/cliconfig/testdata/config.hcl similarity index 60% rename from command/test-fixtures/config.hcl rename to api/cliconfig/testdata/config.hcl index 9161fff452..164acd29cc 100644 --- a/command/test-fixtures/config.hcl +++ b/api/cliconfig/testdata/config.hcl @@ -1,4 +1,4 @@ # Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 +# SPDX-License-Identifier: MPL-2.0 token_helper = "foo" diff --git a/api/cliconfig/util.go b/api/cliconfig/util.go new file mode 100644 index 0000000000..e492ccb038 --- /dev/null +++ b/api/cliconfig/util.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cliconfig + +import ( + "github.com/hashicorp/vault/api/tokenhelper" +) + +// DefaultTokenHelper returns the token helper that is configured for Vault. +// This helper should only be used for non-server CLI commands. +func DefaultTokenHelper() (tokenhelper.TokenHelper, error) { + config, err := loadConfig("") + if err != nil { + return nil, err + } + + path := config.TokenHelper + if path == "" { + return tokenhelper.NewInternalTokenHelper() + } + + path, err = tokenhelper.ExternalTokenHelperPath(path) + if err != nil { + return nil, err + } + return &tokenhelper.ExternalTokenHelper{BinaryPath: path}, nil +} diff --git a/api/go.mod b/api/go.mod index 74b526caca..2338fe9cf6 100644 --- a/api/go.mod +++ b/api/go.mod @@ -20,7 +20,9 @@ require ( github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 github.com/hashicorp/hcl v1.0.0 + github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 + github.com/natefinch/atomic v1.0.1 github.com/stretchr/testify v1.8.4 golang.org/x/net v0.17.0 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 @@ -28,12 +30,11 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.7.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/google/go-cmp v0.5.7 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect - github.com/mattn/go-colorable v0.1.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect golang.org/x/crypto v0.17.0 // indirect diff --git a/api/go.sum b/api/go.sum index 13cac579c0..d231b4b379 100644 --- a/api/go.sum +++ b/api/go.sum @@ -5,8 +5,9 @@ github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= @@ -41,13 +42,14 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -55,6 +57,8 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -79,8 +83,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/command/token/helper.go b/api/tokenhelper/helper.go similarity index 88% rename from command/token/helper.go rename to api/tokenhelper/helper.go index a4bf1fa0f8..a70d3e20a2 100644 --- a/command/token/helper.go +++ b/api/tokenhelper/helper.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package token +package tokenhelper // TokenHelper is an interface that contains basic operations that must be // implemented by a token helper diff --git a/command/token/helper_external.go b/api/tokenhelper/helper_external.go similarity index 94% rename from command/token/helper_external.go rename to api/tokenhelper/helper_external.go index 26e7f44ee8..e9bfb18b8c 100644 --- a/command/token/helper_external.go +++ b/api/tokenhelper/helper_external.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package token +package tokenhelper import ( "bytes" @@ -110,7 +110,7 @@ func (h *ExternalTokenHelper) Path() string { func (h *ExternalTokenHelper) cmd(op string) (*exec.Cmd, error) { script := strings.ReplaceAll(h.BinaryPath, "\\", "\\\\") + " " + op - cmd, err := ExecScript(script) + cmd, err := execScript(script) if err != nil { return nil, err } @@ -118,8 +118,8 @@ func (h *ExternalTokenHelper) cmd(op string) (*exec.Cmd, error) { return cmd, nil } -// ExecScript returns a command to execute a script -func ExecScript(script string) (*exec.Cmd, error) { +// execScript returns a command to execute a script +func execScript(script string) (*exec.Cmd, error) { var shell, flag string if runtime.GOOS == "windows" { shell = "cmd" diff --git a/command/token/helper_external_test.go b/api/tokenhelper/helper_external_test.go similarity index 92% rename from command/token/helper_external_test.go rename to api/tokenhelper/helper_external_test.go index d7b0323602..8928c00402 100644 --- a/command/token/helper_external_test.go +++ b/api/tokenhelper/helper_external_test.go @@ -1,12 +1,11 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package token +package tokenhelper import ( "fmt" "io" - "io/ioutil" "os" "runtime" "strings" @@ -54,10 +53,10 @@ func TestExternalTokenHelperPath(t *testing.T) { } func TestExternalTokenHelper(t *testing.T) { - Test(t, testExternalTokenHelper(t)) + test(t, testExternalTokenHelper()) } -func testExternalTokenHelper(t *testing.T) *ExternalTokenHelper { +func testExternalTokenHelper() *ExternalTokenHelper { return &ExternalTokenHelper{BinaryPath: helperPath("helper"), Env: helperEnv()} } @@ -73,7 +72,7 @@ func helperPath(s ...string) string { func helperEnv() []string { var env []string - tf, err := ioutil.TempFile("", "vault") + tf, err := os.CreateTemp("", "vault") if err != nil { panic(err) } diff --git a/command/token/helper_internal.go b/api/tokenhelper/helper_internal.go similarity index 97% rename from command/token/helper_internal.go rename to api/tokenhelper/helper_internal.go index 866ff1880e..ce95aef07c 100644 --- a/command/token/helper_internal.go +++ b/api/tokenhelper/helper_internal.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package token +package tokenhelper import ( "bytes" diff --git a/command/token/helper_internal_test.go b/api/tokenhelper/helper_internal_test.go similarity index 89% rename from command/token/helper_internal_test.go rename to api/tokenhelper/helper_internal_test.go index 10a7a0cc97..5250da014e 100644 --- a/command/token/helper_internal_test.go +++ b/api/tokenhelper/helper_internal_test.go @@ -1,10 +1,9 @@ // Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MPL-2.0 -package token +package tokenhelper import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -17,11 +16,11 @@ func TestCommand(t *testing.T) { if err != nil { t.Fatal(err) } - Test(t, helper) + test(t, helper) } func TestInternalHelperFilePerms(t *testing.T) { - tmpDir, err := ioutil.TempDir("", t.Name()) + tmpDir, err := os.MkdirTemp("", t.Name()) if err != nil { t.Fatal(err) } diff --git a/api/tokenhelper/testing.go b/api/tokenhelper/testing.go new file mode 100644 index 0000000000..577b4940f3 --- /dev/null +++ b/api/tokenhelper/testing.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tokenhelper + +import ( + "testing" +) + +// test is a public function that can be used in other tests to +// test that a helper is functioning properly. +func test(t *testing.T, h TokenHelper) { + if err := h.Store("foo"); err != nil { + t.Fatalf("err: %s", err) + } + + v, err := h.Get() + if err != nil { + t.Fatalf("err: %s", err) + } + + if v != "foo" { + t.Fatalf("bad: %#v", v) + } + + if err := h.Erase(); err != nil { + t.Fatalf("err: %s", err) + } + + v, err = h.Get() + if err != nil { + t.Fatalf("err: %s", err) + } + + if v != "" { + t.Fatalf("bad: %#v", v) + } +} diff --git a/changelog/25744.txt b/changelog/25744.txt new file mode 100644 index 0000000000..274b228bc2 --- /dev/null +++ b/changelog/25744.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Move CLI token helper functions to importable packages in `api` module. +``` diff --git a/command/base.go b/command/base.go index 5acd2227fa..ed9c76b826 100644 --- a/command/base.go +++ b/command/base.go @@ -19,8 +19,9 @@ import ( "github.com/hashicorp/cli" hcpvlib "github.com/hashicorp/vault-hcp-lib" "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/api/cliconfig" + "github.com/hashicorp/vault/api/tokenhelper" "github.com/hashicorp/vault/command/config" - "github.com/hashicorp/vault/command/token" "github.com/hashicorp/vault/helper/namespace" "github.com/mattn/go-isatty" "github.com/pkg/errors" @@ -72,7 +73,7 @@ type BaseCommand struct { flagHeader map[string]string - tokenHelper token.TokenHelper + tokenHelper tokenhelper.TokenHelper hcpTokenHelper hcpvlib.HCPTokenHelper client *api.Client @@ -248,17 +249,17 @@ func (c *BaseCommand) SetAddress(addr string) { } // SetTokenHelper sets the token helper on the command. -func (c *BaseCommand) SetTokenHelper(th token.TokenHelper) { +func (c *BaseCommand) SetTokenHelper(th tokenhelper.TokenHelper) { c.tokenHelper = th } // TokenHelper returns the token helper attached to the command. -func (c *BaseCommand) TokenHelper() (token.TokenHelper, error) { +func (c *BaseCommand) TokenHelper() (tokenhelper.TokenHelper, error) { if c.tokenHelper != nil { return c.tokenHelper, nil } - helper, err := DefaultTokenHelper() + helper, err := cliconfig.DefaultTokenHelper() if err != nil { return nil, err } diff --git a/command/base_predict.go b/command/base_predict.go index 72ba402fe9..ed3edfa30f 100644 --- a/command/base_predict.go +++ b/command/base_predict.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/api/cliconfig" "github.com/posener/complete" ) @@ -28,7 +29,7 @@ func (p *Predict) Client() *api.Client { client, _ := api.NewClient(nil) if client.Token() == "" { - helper, err := DefaultTokenHelper() + helper, err := cliconfig.DefaultTokenHelper() if err != nil { return } diff --git a/command/config.go b/command/config.go deleted file mode 100644 index 9a5ee7ac57..0000000000 --- a/command/config.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package command - -import ( - "github.com/hashicorp/vault/command/config" -) - -const ( - // DefaultConfigPath is the default path to the configuration file - DefaultConfigPath = "~/.vault" - - // ConfigPathEnv is the environment variable that can be used to - // override where the Vault configuration is. - ConfigPathEnv = "VAULT_CONFIG_PATH" -) - -// Config is the CLI configuration for Vault that can be specified via -// a `$HOME/.vault` file which is HCL-formatted (therefore HCL or JSON). -type DefaultConfig struct { - // TokenHelper is the executable/command that is executed for storing - // and retrieving the authentication token for the Vault CLI. If this - // is not specified, then vault's internal token store will be used, which - // stores the token on disk unencrypted. - TokenHelper string `hcl:"token_helper"` -} - -// Config loads the configuration and returns it. If the configuration -// is already loaded, it is returned. -// -// Config just calls into config.Config for backwards compatibility purposes. -// Use config.Config instead. -func Config() (*DefaultConfig, error) { - conf, err := config.Config() - return (*DefaultConfig)(conf), err -} - -// LoadConfig reads the configuration from the given path. If path is -// empty, then the default path will be used, or the environment variable -// if set. -// -// LoadConfig just calls into config.LoadConfig for backwards compatibility -// purposes. Use config.LoadConfig instead. -func LoadConfig(path string) (*DefaultConfig, error) { - conf, err := config.LoadConfig(path) - return (*DefaultConfig)(conf), err -} - -// ParseConfig parses the given configuration as a string. -// -// ParseConfig just calls into config.ParseConfig for backwards compatibility -// purposes. Use config.ParseConfig instead. -func ParseConfig(contents string) (*DefaultConfig, error) { - conf, err := config.ParseConfig(contents) - return (*DefaultConfig)(conf), err -} diff --git a/command/config/config_test.go b/command/config/config_test.go deleted file mode 100644 index 04cb1be986..0000000000 --- a/command/config/config_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package config - -import ( - "path/filepath" - "reflect" - "strings" - "testing" -) - -const FixturePath = "../test-fixtures" - -func TestLoadConfig(t *testing.T) { - config, err := LoadConfig(filepath.Join(FixturePath, "config.hcl")) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &DefaultConfig{ - TokenHelper: "foo", - } - if !reflect.DeepEqual(expected, config) { - t.Fatalf("bad: %#v", config) - } -} - -func TestLoadConfig_noExist(t *testing.T) { - config, err := LoadConfig("nope/not-once/.never") - if err != nil { - t.Fatal(err) - } - - if config.TokenHelper != "" { - t.Errorf("expected %q to be %q", config.TokenHelper, "") - } -} - -func TestParseConfig_badKeys(t *testing.T) { - _, err := ParseConfig(` -token_helper = "/token" -nope = "true" -`) - if err == nil { - t.Fatal("expected error") - } - - if !strings.Contains(err.Error(), `invalid key "nope" on line 3`) { - t.Errorf("bad error: %s", err.Error()) - } -} diff --git a/command/config/util.go b/command/config/util.go deleted file mode 100644 index 3c6f7ca623..0000000000 --- a/command/config/util.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package config - -import ( - "github.com/hashicorp/vault/command/token" -) - -// DefaultTokenHelper returns the token helper that is configured for Vault. -// This helper should only be used for non-server CLI commands. -func DefaultTokenHelper() (token.TokenHelper, error) { - config, err := LoadConfig("") - if err != nil { - return nil, err - } - - path := config.TokenHelper - if path == "" { - return token.NewInternalTokenHelper() - } - - path, err = token.ExternalTokenHelperPath(path) - if err != nil { - return nil, err - } - return &token.ExternalTokenHelper{BinaryPath: path}, nil -} diff --git a/command/main.go b/command/main.go index d6fad4b6c9..465ec5e6e8 100644 --- a/command/main.go +++ b/command/main.go @@ -18,7 +18,7 @@ import ( "github.com/hashicorp/cli" hcpvlib "github.com/hashicorp/vault-hcp-lib" "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/command/token" + "github.com/hashicorp/vault/api/tokenhelper" "github.com/mattn/go-colorable" ) @@ -135,7 +135,7 @@ func getGlobalFlagValue(arg string) string { } type RunOptions struct { - TokenHelper token.TokenHelper + TokenHelper tokenhelper.TokenHelper HCPTokenHelper hcpvlib.HCPTokenHelper Stdout io.Writer Stderr io.Writer diff --git a/command/token/helper_testing.go b/command/token/helper_testing.go index e948092f45..a536c3c3cc 100644 --- a/command/token/helper_testing.go +++ b/command/token/helper_testing.go @@ -5,9 +5,11 @@ package token import ( "sync" + + "github.com/hashicorp/vault/api/tokenhelper" ) -var _ TokenHelper = (*TestingTokenHelper)(nil) +var _ tokenhelper.TokenHelper = (*TestingTokenHelper)(nil) // TestingTokenHelper implements token.TokenHelper which runs entirely // in-memory. This should not be used outside of testing. diff --git a/command/token/testing.go b/command/token/testing.go deleted file mode 100644 index 24dc3258e5..0000000000 --- a/command/token/testing.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package token - -import ( - "fmt" - "os" - "strings" - "testing" - - "github.com/hashicorp/cli" -) - -// Test is a public function that can be used in other tests to -// test that a helper is functioning properly. -func Test(t *testing.T, h TokenHelper) { - if err := h.Store("foo"); err != nil { - t.Fatalf("err: %s", err) - } - - v, err := h.Get() - if err != nil { - t.Fatalf("err: %s", err) - } - - if v != "foo" { - t.Fatalf("bad: %#v", v) - } - - if err := h.Erase(); err != nil { - t.Fatalf("err: %s", err) - } - - v, err = h.Get() - if err != nil { - t.Fatalf("err: %s", err) - } - - if v != "" { - t.Fatalf("bad: %#v", v) - } -} - -// TestProcess is used to re-execute this test in order to use it as the -// helper process. For this to work, the TestExternalTokenHelperProcess function must -// exist. -func TestProcess(t *testing.T, s ...string) { - h := &ExternalTokenHelper{BinaryPath: TestProcessPath(t, s...)} - Test(t, h) -} - -// TestProcessPath returns the path to the test process. -func TestProcessPath(t *testing.T, s ...string) string { - cs := []string{"-test.run=TestExternalTokenHelperProcess", "--", "GO_WANT_HELPER_PROCESS"} - cs = append(cs, s...) - return fmt.Sprintf( - "%s %s", - os.Args[0], - strings.Join(cs, " ")) -} - -// TestExternalTokenHelperProcessCLI can be called to implement TestExternalTokenHelperProcess -// for TestProcess that just executes a CLI command. -func TestExternalTokenHelperProcessCLI(t *testing.T, cmd cli.Command) { - args := os.Args - for len(args) > 0 { - if args[0] == "--" { - args = args[1:] - break - } - - args = args[1:] - } - if len(args) == 0 || args[0] != "GO_WANT_HELPER_PROCESS" { - return - } - args = args[1:] - - os.Exit(cmd.Run(args)) -} diff --git a/command/util.go b/command/util.go index 717191025d..26fd38000b 100644 --- a/command/util.go +++ b/command/util.go @@ -15,14 +15,14 @@ import ( "github.com/hashicorp/cli" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/command/config" - "github.com/hashicorp/vault/command/token" + "github.com/hashicorp/vault/api/cliconfig" + "github.com/hashicorp/vault/api/tokenhelper" ) // DefaultTokenHelper returns the token helper that is configured for Vault. // This helper should only be used for non-server CLI commands. -func DefaultTokenHelper() (token.TokenHelper, error) { - return config.DefaultTokenHelper() +func DefaultTokenHelper() (tokenhelper.TokenHelper, error) { + return cliconfig.DefaultTokenHelper() } // RawField extracts the raw field from the given data and returns it as a diff --git a/go.mod b/go.mod index 24b1790990..cf91ee14f4 100644 --- a/go.mod +++ b/go.mod @@ -188,7 +188,6 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/reflectwalk v1.0.2 - github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc github.com/ncw/swift v1.0.47 github.com/oklog/run v1.1.0 github.com/okta/okta-sdk-golang/v2 v2.12.1 @@ -464,6 +463,7 @@ require ( github.com/montanaflynn/stats v0.7.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/natefinch/atomic v1.0.1 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect github.com/nwaples/rardecode v1.1.2 // indirect github.com/oklog/ulid v1.3.1 // indirect diff --git a/go.sum b/go.sum index d00da7b5ad..af80468fb5 100644 --- a/go.sum +++ b/go.sum @@ -2969,8 +2969,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc h1:7xGrl4tTpBQu5Zjll08WupHyq+Sp0Z/adtyf1cfk3Q8= -github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc/go.mod h1:1rLVY/DWf3U6vSZgH16S7pymfrhK2lcUlXjgGglw/lY= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/networkplumbing/go-nft v0.2.0/go.mod h1:HnnM+tYvlGAsMU7yoYwXEVLLiDW9gdMmb5HoGcwpuQs=