diff --git a/cmd/security-secretstore-setup/res/configuration.toml b/cmd/security-secretstore-setup/res/configuration.toml index ec496ca234..91bd015497 100644 --- a/cmd/security-secretstore-setup/res/configuration.toml +++ b/cmd/security-secretstore-setup/res/configuration.toml @@ -39,6 +39,8 @@ TokenProvider = "/security-file-token-provider" TokenProviderArgs = [ "-confdir", "res-file-token-provider" ] TokenProviderType = "oneshot" TokenProviderAdminTokenPath = "/run/edgex/secrets/tokenprovider/secrets-token.json" +PasswordProvider = "" +PasswordProviderArgs = [ ] RevokeRootTokens = true [Databases] diff --git a/go.mod b/go.mod index 39219ab573..9b154804cc 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ require ( bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690 github.com/BurntSushi/toml v0.3.1 github.com/OneOfOne/xxhash v1.2.5 - github.com/cloudflare/gokey v0.1.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/edgexfoundry/go-mod-bootstrap v0.0.37 github.com/edgexfoundry/go-mod-configuration v0.0.3 diff --git a/internal/security/secretstore/credentialgenerator.go b/internal/security/secretstore/credentialgenerator.go new file mode 100644 index 0000000000..2a9ac20227 --- /dev/null +++ b/internal/security/secretstore/credentialgenerator.go @@ -0,0 +1,38 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "crypto/rand" + "encoding/base64" +) + +const randomBytesLength = 33 // 264 bits of entropy + +// CredentialGenerator is the interface for pluggable password generators +type CredentialGenerator interface { + Generate(ctx context.Context) (string, error) +} + +type defaultCredentialGenerator struct{} + +// NewDefaultCredentialGenerator generates random passwords as base64-encoded strings +func NewDefaultCredentialGenerator() CredentialGenerator { + return &defaultCredentialGenerator{} +} + +// Generate implementation returns base64-encoded randomBytesLength random bytes +func (cg *defaultCredentialGenerator) Generate(ctx context.Context) (string, error) { + randomBytes := make([]byte, randomBytesLength) + _, err := rand.Read(randomBytes) // all of salt guaranteed to be filled if err==nil + if err != nil { + return "", err + } + newCredential := base64.StdEncoding.EncodeToString(randomBytes) + return newCredential, nil +} diff --git a/internal/security/secretstore/credentialgenerator_test.go b/internal/security/secretstore/credentialgenerator_test.go new file mode 100644 index 0000000000..43896e8afe --- /dev/null +++ b/internal/security/secretstore/credentialgenerator_test.go @@ -0,0 +1,25 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPasswordsAreRandom(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cg := NewDefaultCredentialGenerator() + cred1, err := cg.Generate(ctx) + assert.NoError(t, err) + cred2, err := cg.Generate(ctx) + assert.NoError(t, err) + assert.NotEqual(t, cred1, cred2) + defer cancel() +} diff --git a/internal/security/secretstore/execrunner-mock_test.go b/internal/security/secretstore/execrunner-mock_test.go new file mode 100644 index 0000000000..8314220621 --- /dev/null +++ b/internal/security/secretstore/execrunner-mock_test.go @@ -0,0 +1,47 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "io" + + "github.com/stretchr/testify/mock" +) + +type mockExecRunner struct { + mock.Mock +} + +func (m *mockExecRunner) SetStdout(stdout io.Writer) { + m.Called(stdout) +} + +func (m *mockExecRunner) LookPath(file string) (string, error) { + arguments := m.Called(file) + return arguments.String(0), arguments.Error(1) +} + +func (m *mockExecRunner) CommandContext(ctx context.Context, + name string, arg ...string) CmdRunner { + arguments := m.Called(ctx, name, arg) + return arguments.Get(0).(CmdRunner) +} + +type mockCmd struct { + mock.Mock +} + +func (m *mockCmd) Start() error { + arguments := m.Called() + return arguments.Error(0) +} + +func (m *mockCmd) Wait() error { + arguments := m.Called() + return arguments.Error(0) +} diff --git a/internal/security/secretstore/execrunner.go b/internal/security/secretstore/execrunner.go new file mode 100644 index 0000000000..bde1aa951e --- /dev/null +++ b/internal/security/secretstore/execrunner.go @@ -0,0 +1,59 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "io" + "os" + "os/exec" +) + +// CmdRunner is mockable interface for golang's exec.Cmd +type CmdRunner interface { + Start() error + Wait() error +} + +// ExecRunner is mockable interface for wrapping os/exec functionality +type ExecRunner interface { + SetStdout(stdout io.Writer) + LookPath(file string) (string, error) + CommandContext(ctx context.Context, name string, arg ...string) CmdRunner +} + +type execWrapper struct { + Stdout io.Writer + Stderr io.Writer +} + +// NewDefaultExecRunner creates an os/exec wrapper +// that joins subprocesses' stdout and stderr with the caller's +func NewDefaultExecRunner() ExecRunner { + return &execWrapper{ + Stdout: os.Stdout, + Stderr: os.Stderr, + } +} + +// SetStdout allows overriding of stdout capture (for comsuming password generator output) +func (w *execWrapper) SetStdout(stdout io.Writer) { + w.Stdout = stdout +} + +// LookPath wraps os/exec.LookPath +func (w *execWrapper) LookPath(file string) (string, error) { + return exec.LookPath(file) +} + +// CommandContext wraps os/exec.CommandContext +func (w *execWrapper) CommandContext(ctx context.Context, name string, arg ...string) CmdRunner { + cmd := exec.CommandContext(ctx, name, arg...) + cmd.Stdout = w.Stdout + cmd.Stderr = w.Stderr + return cmd +} diff --git a/internal/security/secretstore/init.go b/internal/security/secretstore/init.go index 9e90937bb8..cac83fb947 100644 --- a/internal/security/secretstore/init.go +++ b/internal/security/secretstore/init.go @@ -230,7 +230,7 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, _ *sync.WaitGroup, _ s } //Step 4: Launch token handler - tokenProvider := NewTokenProvider(ctx, lc, ExecWrapper{}) + tokenProvider := NewTokenProvider(ctx, lc, NewDefaultExecRunner()) if configuration.SecretService.TokenProvider != "" { if err := tokenProvider.SetConfiguration(configuration.SecretService); err != nil { lc.Error(fmt.Sprintf("failed to configure token provider: %s", err.Error())) @@ -252,9 +252,8 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, _ *sync.WaitGroup, _ s } // credential creation - gk := NewGokeyGenerator(rootToken) - lc.Warn("WARNING: The gokey generator is a reference implementation for credential generation and the underlying libraries not been reviewed for cryptographic security. The user is encouraged to perform their own security investigation before deployment.") - cred := NewCred(req, rootToken, gk, configuration.SecretService.GetSecretSvcBaseURL(), lc) + gen := NewPasswordGenerator(lc, configuration.SecretService.PasswordProvider, configuration.SecretService.PasswordProviderArgs) + cred := NewCred(req, rootToken, gen, configuration.SecretService.GetSecretSvcBaseURL(), lc) // continue credential creation @@ -270,7 +269,7 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, _ *sync.WaitGroup, _ s // Redis 5.x only supports a single shared password. When Redis 6 is released, this can be updated // to a per service password. - redis5Password, err := cred.GeneratePassword("redis5") + redis5Password, err := cred.GeneratePassword(ctx) if err != nil { lc.Error("failed to generate redis5 password") os.Exit(1) @@ -283,7 +282,7 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, _ *sync.WaitGroup, _ s for dbname, info := range configuration.Databases { service := info.Service // generate credentials - password, err := cred.GeneratePassword(dbname) + password, err := cred.GeneratePassword(ctx) if err != nil { lc.Error(fmt.Sprintf("failed to generate credential pair for service %s", service)) os.Exit(1) diff --git a/internal/security/secretstore/password.go b/internal/security/secretstore/password.go index 6c7e7c6254..2eb6a2ca7a 100644 --- a/internal/security/secretstore/password.go +++ b/internal/security/secretstore/password.go @@ -17,6 +17,7 @@ package secretstore import ( "bytes" + "context" "encoding/json" "fmt" "io/ioutil" @@ -26,36 +27,34 @@ import ( "github.com/edgexfoundry/edgex-go/internal" "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" - - "github.com/cloudflare/gokey" ) -// CredentialGenerator returns a credential generated with random algorithm for secret store -type CredentialGenerator interface { - Generate(string) (string, error) -} - -// GokeyGenerator implements the CredentialGenerator interface using the gokey library -// using tokenPath as the gokey master password and accepting the realm as the argument -// to the Generate method -type GokeyGenerator struct { - masterPassword string +type passwordGenerator struct { + generatorImplementation CredentialGenerator } -func NewGokeyGenerator(masterPassword string) *GokeyGenerator { - return &GokeyGenerator{masterPassword: masterPassword} +// NewPasswordGenerator wires up a pluggable password generator +// or defaults to a built-in implementation if +// the pluggable configuration is missing +func NewPasswordGenerator(lc logger.LoggingClient, passwordProvider string, passwordProviderArgs []string) CredentialGenerator { + gk := &passwordGenerator{ + generatorImplementation: NewDefaultCredentialGenerator(), + } + if passwordProvider != "" { + pp := NewPasswordProvider(lc, NewDefaultExecRunner()) + err := pp.SetConfiguration(passwordProvider, passwordProviderArgs) + if err != nil { + lc.Warn(fmt.Sprintf("Could not configure password generator %s: error: %s", passwordProvider, err.Error())) + return gk // fall-back to builtin + } + gk.generatorImplementation = pp + } + return gk } -func (gk GokeyGenerator) Generate(realm string) (string, error) { - passSpec := gokey.PasswordSpec{ - Length: 16, - Upper: 3, - Lower: 3, - Digits: 2, - Special: 1, - AllowedSpecial: "", - } - return gokey.GetPass(gk.masterPassword, realm, nil, &passSpec) +// Generate delegates password generation to underlying implementation +func (gk *passwordGenerator) Generate(ctx context.Context) (string, error) { + return gk.generatorImplementation.Generate(ctx) } type CredCollect struct { @@ -174,8 +173,9 @@ func (cr *Cred) credPathURL(path string) (string, error) { return fullURL.String(), nil } -func (cr *Cred) GeneratePassword(service string) (string, error) { - return cr.generator.Generate(service) +// GeneratePassword is a pass-through to the password generator +func (cr *Cred) GeneratePassword(ctx context.Context) (string, error) { + return cr.generator.Generate(ctx) } func (cr *Cred) UploadToStore(pair *UserPasswordPair, path string) error { diff --git a/internal/security/secretstore/password_linux_test.go b/internal/security/secretstore/password_linux_test.go new file mode 100644 index 0000000000..0f5e14e4dd --- /dev/null +++ b/internal/security/secretstore/password_linux_test.go @@ -0,0 +1,36 @@ +// +build linux + +// +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "net/http" + "testing" + + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenerateWithAPG(t *testing.T) { + rootToken := "s.Ga5jyNq6kNfRMVQk2LY1j9iu" + mockLogger := logger.MockLogger{} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Note: apg only available with gnome-desktop, expected to be missing on server Linux distros + gk := NewPasswordGenerator(mockLogger, "apg", []string{"-a", "1", "-n", "1", "-m", "12", "-x", "64"}) + cr := NewCred(&http.Client{}, rootToken, gk, "", logger.MockLogger{}) + + p1, err := cr.GeneratePassword(ctx) + require.NoError(t, err, "failed to create credential") + p2, err := cr.GeneratePassword(ctx) + require.NoError(t, err, "failed to create credential") + assert.NotEqual(t, p1, p2, "each call to GeneratePassword should return a new password") +} diff --git a/internal/security/secretstore/password_test.go b/internal/security/secretstore/password_test.go index d00ff84a87..d5715d9ceb 100644 --- a/internal/security/secretstore/password_test.go +++ b/internal/security/secretstore/password_test.go @@ -16,6 +16,7 @@ package secretstore import ( + "context" "fmt" "net/http" "net/http/httptest" @@ -27,29 +28,24 @@ import ( "github.com/edgexfoundry/edgex-go/internal/security/secretstoreclient" "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestGenerate(t *testing.T) { +func TestGenerateWithDefaults(t *testing.T) { rootToken := "s.Ga5jyNq6kNfRMVQk2LY1j9iu" - gk := NewGokeyGenerator(rootToken) + mockLogger := logger.MockLogger{} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + gk := NewPasswordGenerator(mockLogger, "", []string{}) cr := NewCred(&http.Client{}, rootToken, gk, "", logger.MockLogger{}) - realm1 := "service1" - realm2 := "service2" - - p1, err := cr.GeneratePassword(realm1) - if err != nil { - t.Errorf("failed to create credential") - t.Errorf(err.Error()) - } - p2, err := cr.GeneratePassword(realm2) - if err != nil { - t.Errorf("failed to create credential") - t.Errorf(err.Error()) - } - if p1 == p2 { - t.Errorf("error: different master key and realm combination need to generate different passwords") - } + p1, err := cr.GeneratePassword(ctx) + require.NoError(t, err, "failed to create credential") + p2, err := cr.GeneratePassword(ctx) + require.NoError(t, err, "failed to create credential") + assert.NotEqual(t, p1, p2, "each call to GeneratePassword should return a new password") } func TestRetrieveCred(t *testing.T) { @@ -95,7 +91,7 @@ func TestRetrieveCred(t *testing.T) { cr := NewCred( secretstoreclient.NewRequestor(mockLogger).Insecure(), "token", - NewGokeyGenerator(""), + NewPasswordGenerator(mockLogger, "", []string{}), configuration.SecretService.GetSecretSvcBaseURL(), mockLogger) pair, err := cr.retrieve(credPath) diff --git a/internal/security/secretstore/passwordprovider.go b/internal/security/secretstore/passwordprovider.go new file mode 100644 index 0000000000..1af32aba06 --- /dev/null +++ b/internal/security/secretstore/passwordprovider.go @@ -0,0 +1,87 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "bytes" + "context" + "fmt" + "os/exec" + "strings" + "syscall" + + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" +) + +type PasswordProvider struct { + loggingClient logger.LoggingClient + execRunner ExecRunner + initialized bool + passwordProvider string + passwordProviderArgs []string + resolvedPath string +} + +// NewPasswordProvider creates a new PasswordProvider +func NewPasswordProvider(lc logger.LoggingClient, execRunner ExecRunner) *PasswordProvider { + return &PasswordProvider{ + loggingClient: lc, + execRunner: execRunner, + } +} + +// SetConfiguration parses token provider configuration and resolves paths specified therein +func (p *PasswordProvider) SetConfiguration(passwordProvider string, passwordProviderArgs []string) error { + var err error + p.passwordProvider = passwordProvider + p.passwordProviderArgs = passwordProviderArgs + resolvedPath, err := p.execRunner.LookPath(p.passwordProvider) + if err != nil { + p.loggingClient.Error(fmt.Sprintf("Failed to locate %s on PATH: %s", p.passwordProvider, err.Error())) + return err + } + p.initialized = true + p.resolvedPath = resolvedPath + return nil +} + +// Generate retrives the password from the tool +func (p *PasswordProvider) Generate(ctx context.Context) (string, error) { + var outputBuffer bytes.Buffer + + if !p.initialized { + err := fmt.Errorf("PasswordProvider object not initialized; call SetConfiguration() first") + return "", err + } + + p.execRunner.SetStdout(&outputBuffer) + + p.loggingClient.Info(fmt.Sprintf("Launching password provider %s with arguments %s", p.resolvedPath, strings.Join(p.passwordProviderArgs, " "))) + cmd := p.execRunner.CommandContext(ctx, p.resolvedPath, p.passwordProviderArgs...) + if err := cmd.Start(); err != nil { + // For example, this might occur if a shared library was missing + p.loggingClient.Error(fmt.Sprintf("%s failed to launch: %s", p.resolvedPath, err.Error())) + return "", err + } + + err := cmd.Wait() + if exitError, ok := err.(*exec.ExitError); ok { + waitStatus := exitError.Sys().(syscall.WaitStatus) + p.loggingClient.Error(fmt.Sprintf("%s terminated with non-zero exit code %d", p.resolvedPath, waitStatus.ExitStatus())) + return "", err + } + if err != nil { + p.loggingClient.Error(fmt.Sprintf("%s failed with unexpected error: %s", p.resolvedPath, err.Error())) + return "", err + } + + p.loggingClient.Info("password provider exited successfully") + + pw := string(outputBuffer.Bytes()) + pw = strings.TrimSuffix(pw, "\n") + return pw, nil +} diff --git a/internal/security/secretstore/passwordprovider_test.go b/internal/security/secretstore/passwordprovider_test.go new file mode 100644 index 0000000000..68bea25b16 --- /dev/null +++ b/internal/security/secretstore/passwordprovider_test.go @@ -0,0 +1,97 @@ +// +// Copyright (c) 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package secretstore + +import ( + "context" + "errors" + "os" + "os/exec" + "testing" + + "github.com/edgexfoundry/edgex-go/internal/security/secretstoreclient" + "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestInvalidPasswordProvider(t *testing.T) { + config := secretstoreclient.SecretServiceInfo{ + PasswordProvider: "does-not-exist", + } + mockExecRunner := &mockExecRunner{} + mockExecRunner.On("LookPath", config.PasswordProvider). + Return("", errors.New("fake file does not exist")) + _, cancel := context.WithCancel(context.Background()) + defer cancel() + p := NewPasswordProvider(logger.MockLogger{}, mockExecRunner) + err := p.SetConfiguration(config.PasswordProvider, config.PasswordProviderArgs) + assert.Error(t, err) + mockExecRunner.AssertExpectations(t) +} + +func TestPasswordConfigNotInitialized(t *testing.T) { + mockExecRunner := &mockExecRunner{} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + p := NewPasswordProvider(logger.MockLogger{}, mockExecRunner) + // SetConfiguration deliberately missing + _, err := p.Generate(ctx) + assert.Error(t, err) + mockExecRunner.AssertExpectations(t) +} + +func TestPasswordProviderFailsToStart(t *testing.T) { + config := secretstoreclient.SecretServiceInfo{ + PasswordProvider: "failing-executable", + PasswordProviderArgs: []string{"arg1", "arg2"}, + } + mockExecRunner := &mockExecRunner{} + mockCmd := mockCmd{} + mockExecRunner.On("LookPath", config.PasswordProvider). + Return(config.PasswordProvider, nil) + mockExecRunner.On("SetStdout", mock.Anything).Once() + mockExecRunner.On("CommandContext", mock.Anything, + config.PasswordProvider, config.PasswordProviderArgs). + Return(&mockCmd) + mockCmd.On("Start").Return(errors.New("error starting")) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + p := NewPasswordProvider(logger.MockLogger{}, mockExecRunner) + err := p.SetConfiguration(config.PasswordProvider, config.PasswordProviderArgs) + assert.NoError(t, err) + _, err = p.Generate(ctx) + assert.Error(t, err) + mockExecRunner.AssertExpectations(t) +} + +func TestPasswordProviderFailsAtRuntime(t *testing.T) { + config := secretstoreclient.SecretServiceInfo{ + PasswordProvider: "failing-executable", + PasswordProviderArgs: []string{"arg1", "arg2"}, + } + mockExecRunner := &mockExecRunner{} + mockCmd := mockCmd{} + mockExecRunner.On("LookPath", config.PasswordProvider). + Return(config.PasswordProvider, nil) + mockExecRunner.On("SetStdout", mock.Anything).Once() + mockExecRunner.On("CommandContext", mock.Anything, + config.PasswordProvider, config.PasswordProviderArgs). + Return(&mockCmd) + mockCmd.On("Start").Return(nil) + mockCmd.On("Wait").Return(&exec.ExitError{ProcessState: &os.ProcessState{}}) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + p := NewPasswordProvider(logger.MockLogger{}, mockExecRunner) + err := p.SetConfiguration(config.PasswordProvider, config.PasswordProviderArgs) + assert.NoError(t, err) + _, err = p.Generate(ctx) + assert.Error(t, err) + mockExecRunner.AssertExpectations(t) +} diff --git a/internal/security/secretstore/tokenprovider.go b/internal/security/secretstore/tokenprovider.go index 87f7851e8b..a0924a5bd6 100644 --- a/internal/security/secretstore/tokenprovider.go +++ b/internal/security/secretstore/tokenprovider.go @@ -19,7 +19,6 @@ package secretstore import ( "context" "fmt" - "os" "os/exec" "strings" "syscall" @@ -30,29 +29,6 @@ import ( const OneShotProvider = "oneshot" -type ExecRunner interface { - LookPath(file string) (string, error) - CommandContext(ctx context.Context, name string, arg ...string) CmdRunner -} - -type CmdRunner interface { - Start() error - Wait() error -} - -type ExecWrapper struct{} - -func (w ExecWrapper) LookPath(file string) (string, error) { - return exec.LookPath(file) -} - -func (w ExecWrapper) CommandContext(ctx context.Context, name string, arg ...string) CmdRunner { - cmd := exec.CommandContext(ctx, name, arg...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd -} - type TokenProvider struct { loggingClient logger.LoggingClient ctx context.Context diff --git a/internal/security/secretstore/tokenprovider_linux_test.go b/internal/security/secretstore/tokenprovider_linux_test.go index d75ebd74bf..156515f4bb 100644 --- a/internal/security/secretstore/tokenprovider_linux_test.go +++ b/internal/security/secretstore/tokenprovider_linux_test.go @@ -43,7 +43,7 @@ func TestCreatesFile(t *testing.T) { err := os.RemoveAll(testfile) defer os.RemoveAll(testfile) // cleanup - p := NewTokenProvider(ctx, logger.MockLogger{}, ExecWrapper{}) + p := NewTokenProvider(ctx, logger.MockLogger{}, NewDefaultExecRunner()) p.SetConfiguration(config) assert.NoError(t, err) diff --git a/internal/security/secretstore/tokenprovider_test.go b/internal/security/secretstore/tokenprovider_test.go index 4117beffbb..b1069db1ff 100644 --- a/internal/security/secretstore/tokenprovider_test.go +++ b/internal/security/secretstore/tokenprovider_test.go @@ -58,7 +58,7 @@ func TestInvalidProviderType(t *testing.T) { func TestNoConfig(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - p := NewTokenProvider(ctx, logger.MockLogger{}, ExecWrapper{}) + p := NewTokenProvider(ctx, logger.MockLogger{}, NewDefaultExecRunner()) // don't call SetConfiguration() err := p.Launch() defer cancel() @@ -117,32 +117,3 @@ func testCommon(config secretstoreclient.SecretServiceInfo, mockExecRunner ExecR } return cancel, p.Launch() } - -type mockExecRunner struct { - mock.Mock -} - -func (m *mockExecRunner) LookPath(file string) (string, error) { - arguments := m.Called(file) - return arguments.String(0), arguments.Error(1) -} - -func (m *mockExecRunner) CommandContext(ctx context.Context, - name string, arg ...string) CmdRunner { - arguments := m.Called(ctx, name, arg) - return arguments.Get(0).(CmdRunner) -} - -type mockCmd struct { - mock.Mock -} - -func (m *mockCmd) Start() error { - arguments := m.Called() - return arguments.Error(0) -} - -func (m *mockCmd) Wait() error { - arguments := m.Called() - return arguments.Error(0) -} diff --git a/internal/security/secretstoreclient/types.go b/internal/security/secretstoreclient/types.go index 373a4a0f5e..4a4e1b887d 100644 --- a/internal/security/secretstoreclient/types.go +++ b/internal/security/secretstoreclient/types.go @@ -37,6 +37,8 @@ type SecretServiceInfo struct { TokenProviderArgs []string TokenProviderType string TokenProviderAdminTokenPath string + PasswordProvider string + PasswordProviderArgs []string RevokeRootTokens bool }