Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

operator generate-root -decode: allow token from stdin #12881

Merged
merged 3 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/12881.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
command: operator generate-root -decode: allow passing encoded token via stdin
```
24 changes: 23 additions & 1 deletion command/operator_generate_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ func (c *OperatorGenerateRootCommand) Flags() *FlagSets {
Default: "",
EnvVar: "",
Completion: complete.PredictAnything,
Usage: "The value to decode; setting this triggers a decode operation.",
Usage: "The value to decode; setting this triggers a decode operation. " +
" If the value is \"-\" then read the encoded token from stdin.",
})

f.BoolVar(&BoolVar{
Expand Down Expand Up @@ -328,6 +329,27 @@ func (c *OperatorGenerateRootCommand) decode(client *api.Client, encoded, otp st
return 1
}

if encoded == "-" {
// Pull our fake stdin if needed
stdin := (io.Reader)(os.Stdin)
if c.testStdin != nil {
stdin = c.testStdin
}

var buf bytes.Buffer
if _, err := io.Copy(&buf, stdin); err != nil {
c.UI.Error(fmt.Sprintf("Failed to read from stdin: %s", err))
return 1
}

encoded = buf.String()
ccapurso marked this conversation as resolved.
Show resolved Hide resolved

if encoded == "" {
c.UI.Error("Missing encoded value. When using -decode=\"-\" value must be passed via stdin.")
return 1
}
}

f := client.Sys().GenerateRootStatus
switch kind {
case generateRootDR:
Expand Down
91 changes: 91 additions & 0 deletions command/operator_generate_root_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !race
// +build !race

package command
Expand Down Expand Up @@ -158,6 +159,96 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) {
}
})

t.Run("decode_from_stdin", func(t *testing.T) {
t.Parallel()

encoded := "Bxg9JQQqOCNKBRICNwMIRzo2J3cWCBRi"
otp := "3JhHkONiyiaNYj14nnD9xZQS"

client, closer := testVaultServer(t)
defer closer()

stdinR, stdinW := io.Pipe()
go func() {
stdinW.Write([]byte(encoded))
stdinW.Close()
}()

ui, cmd := testOperatorGenerateRootCommand(t)
cmd.client = client
cmd.testStdin = stdinR

// Simulate piped output to print raw output
old := os.Stdout
_, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = w

code := cmd.Run([]string{
"-decode", "-", // read from stdin
"-otp", otp,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}

w.Close()
os.Stdout = old

expected := "4RUmoevJ3lsLni9sTXcNnRE1"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if combined != expected {
t.Errorf("expected %q to be %q", combined, expected)
}
})

t.Run("decode_from_stdin_empty", func(t *testing.T) {
t.Parallel()

encoded := ""
otp := "3JhHkONiyiaNYj14nnD9xZQS"

client, closer := testVaultServer(t)
defer closer()

stdinR, stdinW := io.Pipe()
go func() {
stdinW.Write([]byte(encoded))
stdinW.Close()
}()

ui, cmd := testOperatorGenerateRootCommand(t)
cmd.client = client
cmd.testStdin = stdinR

// Simulate piped output to print raw output
old := os.Stdout
_, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stdout = w

code := cmd.Run([]string{
"-decode", "-", // read from stdin
"-otp", otp,
})
if exp := 1; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}

w.Close()
os.Stdout = old

expected := "Missing encoded value"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})

t.Run("cancel", func(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions website/content/docs/commands/operator/generate-root.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ flags](/docs/commands) included on all commands.

- `-decode` `(string: "")` - Decode and output the generated root token. This
option requires the `-otp` flag be set to the OTP used during initialization.
If value is "-" then read the encoded token from stdin.

- `-generate-otp` `(bool: false)` - Generate and print a high-entropy
one-time-password (OTP) suitable for use with the "-init" flag.
Expand Down