forked from edgexfoundry/edgex-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implement pluggable password generator (edgexfoundry#2659)
Remove dependency on GoKey for password generator; if not specified, the built-in password generator does the equivalent of "openssl rand -base64 33" which generates a base64 encoded random string of 264 random bits. Otherwise, the code looks for a PasswordProvider in configuration that points to an executable, and PasswordProviderArgs that specify the arguments to that executable. The code then launches that exectuable, trims the trailing newline, and uses the result as a password. Signed-off-by: Bryon Nevis <[email protected]>
- Loading branch information
Showing
16 changed files
with
441 additions
and
107 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |
Oops, something went wrong.