Update token-lookup command

This commit is contained in:
Seth Vargo 2017-09-05 00:05:01 -04:00
parent ad28d1d82a
commit 5cd62171f9
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
2 changed files with 261 additions and 166 deletions

View File

@ -5,96 +5,126 @@ import (
"strings"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/meta"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
// TokenLookupCommand is a Command that outputs details about the
// provided.
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*TokenLookupCommand)(nil)
var _ cli.CommandAutocomplete = (*TokenLookupCommand)(nil)
// TokenLookupCommand is a Command that outputs details about the provided.
type TokenLookupCommand struct {
meta.Meta
*BaseCommand
flagAccessor bool
}
func (c *TokenLookupCommand) Synopsis() string {
return "Displays information about a token"
}
func (c *TokenLookupCommand) Help() string {
helpText := `
Usage: vault token-lookup [options] [TOKEN | ACCESSOR]
Displays information about a token or accessor. If a TOKEN is not provided,
the locally authenticated token is used.
Get information about the locally authenticated token (this uses the
/auth/token/lookup-self endpoint and permission):
$ vault token-lookup
Get information about a particular token (this uses the /auth/token/lookup
endpoint and permission):
$ vault token-lookup 96ddf4bc-d217-f3ba-f9bd-017055595017
Get information about a token via its accessor:
$ vault token-lookup -accessor 9793c9b3-e04a-46f3-e7b8-748d7da248da
For a full list of examples, please see the documentation.
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *TokenLookupCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
f.BoolVar(&BoolVar{
Name: "accessor",
Target: &c.flagAccessor,
Default: false,
EnvVar: "",
Completion: complete.PredictNothing,
Usage: "Treat the argument as an accessor intead of a token. When " +
"this option is selected, the output will NOT include the token.",
})
return set
}
func (c *TokenLookupCommand) AutocompleteArgs() complete.Predictor {
return c.PredictVaultFiles()
}
func (c *TokenLookupCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *TokenLookupCommand) Run(args []string) int {
var format string
var accessor bool
flags := c.Meta.FlagSet("token-lookup", meta.FlagSetDefault)
flags.BoolVar(&accessor, "accessor", false, "")
flags.StringVar(&format, "format", "table", "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
args = flags.Args()
if len(args) > 1 {
flags.Usage()
c.Ui.Error(fmt.Sprintf(
"\ntoken-lookup expects at most one argument"))
token := ""
args = f.Args()
switch {
case c.flagAccessor && len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments with -accessor (expected 1, got %d)", len(args)))
return 1
case c.flagAccessor && len(args) > 1:
c.UI.Error(fmt.Sprintf("Too many arguments with -accessor (expected 1, got %d)", len(args)))
return 1
case len(args) == 0:
// Use the local token
case len(args) == 1:
token = strings.TrimSpace(args[0])
case len(args) > 1:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0-1, got %d)", len(args)))
return 1
}
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"error initializing client: %s", err))
c.UI.Error(err.Error())
return 2
}
var secret *api.Secret
switch {
case !accessor && len(args) == 0:
case token == "":
secret, err = client.Auth().Token().LookupSelf()
case !accessor && len(args) == 1:
secret, err = client.Auth().Token().Lookup(args[0])
case accessor && len(args) == 1:
secret, err = client.Auth().Token().LookupAccessor(args[0])
case c.flagAccessor:
secret, err = client.Auth().Token().LookupAccessor(token)
default:
// This happens only when accessor is set and no argument is passed
c.Ui.Error(fmt.Sprintf("token-lookup expects an argument when accessor flag is set"))
return 1
secret, err = client.Auth().Token().Lookup(token)
}
if err != nil {
c.Ui.Error(fmt.Sprintf(
"error looking up token: %s", err))
return 1
}
return OutputSecret(c.Ui, format, secret)
}
func doTokenLookup(args []string, client *api.Client) (*api.Secret, error) {
if len(args) == 0 {
return client.Auth().Token().LookupSelf()
c.UI.Error(fmt.Sprintf("Error looking up token: %s", err))
return 2
}
token := args[0]
return client.Auth().Token().Lookup(token)
}
func (c *TokenLookupCommand) Synopsis() string {
return "Display information about the specified token"
}
func (c *TokenLookupCommand) Help() string {
helpText := `
Usage: vault token-lookup [options] [token|accessor]
Displays information about the specified token. If no token is specified, the
operation is performed on the currently authenticated token i.e. lookup-self.
Information about the token can be retrieved using the token accessor via the
'-accessor' flag.
General Options:
` + meta.GeneralOptionsUsage() + `
Token Lookup Options:
-accessor A boolean flag, if set, treats the argument as an accessor of the token.
Note that the response of the command when this is set, will not contain
the token ID. Accessor is only meant for looking up the token properties
(and for revocation via '/auth/token/revoke-accessor/<accessor>' endpoint).
-format=table The format for output. By default it is a whitespace-
delimited table. This can also be json or yaml.
`
return strings.TrimSpace(helpText)
return OutputSecret(c.UI, c.flagFormat, secret)
}

View File

@ -1,124 +1,189 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/meta"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
func TestTokenLookupAccessor(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
func testTokenLookupCommand(tb testing.TB) (*cli.MockUi, *TokenLookupCommand) {
tb.Helper()
ui := new(cli.MockUi)
c := &TokenLookupCommand{
Meta: meta.Meta{
ClientToken: token,
Ui: ui,
ui := cli.NewMockUi()
return ui, &TokenLookupCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
args := []string{
"-address", addr,
}
c.Run(args)
}
// Create a new token for us to use
client, err := c.Client()
if err != nil {
t.Fatalf("err: %s", err)
func TestTokenLookupCommand_Run(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
out string
code int
}{
{
"accessor_no_args",
[]string{"-accessor"},
"Not enough arguments",
1,
},
{
"accessor_too_many_args",
[]string{"-accessor", "abcd1234", "efgh5678"},
"Too many arguments",
1,
},
{
"too_many_args",
[]string{"abcd1234", "efgh5678"},
"Too many arguments",
1,
},
{
"format",
[]string{"-format", "json"},
"{",
0,
},
{
"format_bad",
[]string{"-format", "nope-not-real"},
"Invalid output format",
1,
},
}
resp, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Lease: "1h",
t.Run("validations", func(t *testing.T) {
t.Parallel()
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
ui, cmd := testTokenLookupCommand(t)
cmd.client = client
code := cmd.Run(tc.args)
if code != tc.code {
t.Errorf("expected %d to be %d", code, tc.code)
}
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, tc.out) {
t.Errorf("expected %q to contain %q", combined, tc.out)
}
})
}
})
if err != nil {
t.Fatalf("err: %s", err)
}
// Enable the accessor flag
args = append(args, "-accessor")
t.Run("token", func(t *testing.T) {
t.Parallel()
// Expect failure if no argument is passed when accessor flag is set
code := c.Run(args)
if code == 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
client, closer := testVaultServer(t)
defer closer()
// Add token accessor as arg
args = append(args, resp.Auth.Accessor)
code = c.Run(args)
if code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
}
token, _ := testTokenAndAccessor(t, client)
func TestTokenLookupSelf(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
ui, cmd := testTokenLookupCommand(t)
cmd.client = client
ui := new(cli.MockUi)
c := &TokenLookupCommand{
Meta: meta.Meta{
ClientToken: token,
Ui: ui,
},
}
code := cmd.Run([]string{
token,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
args := []string{
"-address", addr,
}
// Run it against itself
code := c.Run(args)
// Verify it worked
if code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
}
func TestTokenLookup(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
ui := new(cli.MockUi)
c := &TokenLookupCommand{
Meta: meta.Meta{
ClientToken: token,
Ui: ui,
},
}
args := []string{
"-address", addr,
}
// Run it once for client
c.Run(args)
// Create a new token for us to use
client, err := c.Client()
if err != nil {
t.Fatalf("err: %s", err)
}
resp, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Lease: "1h",
expected := token
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
if err != nil {
t.Fatalf("err: %s", err)
}
// Add token as arg for real test and run it
args = append(args, resp.Auth.ClientToken)
code := c.Run(args)
t.Run("self", func(t *testing.T) {
t.Parallel()
// Verify it worked
if code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
client, closer := testVaultServer(t)
defer closer()
ui, cmd := testTokenLookupCommand(t)
cmd.client = client
code := cmd.Run([]string{})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "display_name"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
t.Run("accessor", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
_, accessor := testTokenAndAccessor(t, client)
ui, cmd := testTokenLookupCommand(t)
cmd.client = client
code := cmd.Run([]string{
"-accessor",
accessor,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := accessor
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServerBad(t)
defer closer()
ui, cmd := testTokenLookupCommand(t)
cmd.client = client
code := cmd.Run([]string{})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error looking up token: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
t.Run("no_tabs", func(t *testing.T) {
t.Parallel()
_, cmd := testTokenLookupCommand(t)
assertNoTabs(t, cmd)
})
}