Skip to content

Commit

Permalink
Move CLI token helper to api module (hashicorp#25744)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
tomhjp authored Mar 4, 2024
1 parent 087efc9 commit 9ed0082
Show file tree
Hide file tree
Showing 25 changed files with 163 additions and 310 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Vagrantfile
!command/agent/config/test-fixtures/*.hcl
!command/server/test-fixtures/**/*.hcl
!enos/**/*.hcl
!**/testdata/*.hcl

# Enos
.enos
Expand Down
68 changes: 32 additions & 36 deletions command/config/config.go → api/cliconfig/config.go
Original file line number Diff line number Diff line change
@@ -1,58 +1,45 @@
// 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
// 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.
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
}

Expand All @@ -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
Expand All @@ -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",
valid := map[string]struct{}{
"token_helper": {},
}
if err := hclutil.CheckHCLKeys(list, valid); err != nil {
return nil, err

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
var c defaultConfig
if err := hcl.DecodeObject(&c, list); err != nil {
return nil, err
}
Expand Down
14 changes: 6 additions & 8 deletions command/config_test.go → api/cliconfig/config_test.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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) {
Expand All @@ -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)
}
Expand All @@ -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"
`)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
# SPDX-License-Identifier: MPL-2.0

token_helper = "foo"
28 changes: 28 additions & 0 deletions api/cliconfig/util.go
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 5 additions & 4 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ 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
)

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
Expand Down
18 changes: 11 additions & 7 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -41,20 +42,23 @@ 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=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
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=
Expand All @@ -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=
Expand Down
4 changes: 2 additions & 2 deletions command/token/helper.go → api/tokenhelper/helper.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -110,16 +110,16 @@ 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
}
cmd.Env = h.Env
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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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()}
}

Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
Loading

0 comments on commit 9ed0082

Please sign in to comment.