diff --git a/api/auth_token.go b/api/auth_token.go index 78dc98aac..f65c0a7c5 100644 --- a/api/auth_token.go +++ b/api/auth_token.go @@ -25,6 +25,18 @@ func (c *TokenAuth) Create(opts *TokenCreateRequest) (*Secret, error) { return ParseSecret(resp.Body) } +func (c *TokenAuth) Lookup(token string) (*Secret, error) { + r := c.c.NewRequest("GET", "/v1/auth/token/lookup/"+token) + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return ParseSecret(resp.Body) +} + func (c *TokenAuth) LookupSelf() (*Secret, error) { r := c.c.NewRequest("GET", "/v1/auth/token/lookup-self") diff --git a/api/auth_token_test.go b/api/auth_token_test.go index f2544a632..4e92f1043 100644 --- a/api/auth_token_test.go +++ b/api/auth_token_test.go @@ -34,6 +34,40 @@ func TestAuthTokenCreate(t *testing.T) { } } +func TestAuthTokenLookup(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + config := DefaultConfig() + config.Address = addr + + client, err := NewClient(config) + if err != nil { + t.Fatal(err) + } + client.SetToken(token) + + // Create a new token ... + secret2, err := client.Auth().Token().Create(&TokenCreateRequest{ + Lease: "1h", + }) + if err != nil { + t.Fatal(err) + } + + // lookup details of this token + secret, err := client.Auth().Token().Lookup(secret2.Auth.ClientToken) + if err != nil { + t.Fatalf("unable to lookup details of token, err = %v", err) + } + + if secret.Data["id"] != secret2.Auth.ClientToken { + t.Errorf("Did not get back details about our provided token, id returned=%s", secret.Data["id"]) + } + +} + func TestAuthTokenLookupSelf(t *testing.T) { core, _, token := vault.TestCoreUnsealed(t) ln, addr := http.TestServer(t, core) diff --git a/cli/commands.go b/cli/commands.go index 8fa09dbf4..ffaeeea01 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -254,6 +254,12 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory { }, nil }, + "token-lookup": func() (cli.Command, error) { + return &command.TokenLookupCommand{ + Meta: meta, + }, nil + }, + "token-renew": func() (cli.Command, error) { return &command.TokenRenewCommand{ Meta: meta, diff --git a/command/token_lookup.go b/command/token_lookup.go new file mode 100644 index 000000000..6443904d2 --- /dev/null +++ b/command/token_lookup.go @@ -0,0 +1,80 @@ +package command + +import ( + "fmt" + "github.com/hashicorp/vault/api" + "strings" +) + +// TokenLookupCommand is a Command that outputs details about the +// provided. +type TokenLookupCommand struct { + Meta +} + +func (c *TokenLookupCommand) Run(args []string) int { + var format string + flags := c.Meta.FlagSet("token-lookup", FlagSetDefault) + flags.StringVar(&format, "format", "table", "") + flags.Usage = func() { c.Ui.Error(c.Help()) } + if err := flags.Parse(args); err != nil { + return 1 + } + + args = flags.Args() + if len(args) > 1 { + flags.Usage() + c.Ui.Error(fmt.Sprintf( + "\ntoken-lookup expects at most one argument")) + return 1 + } + + client, err := c.Client() + if err != nil { + c.Ui.Error(fmt.Sprintf( + "Error initializing client: %s", err)) + return 2 + } + + secret, err := doTokenLookup(args, client) + 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() + } + + 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] + + Displays information about the specified token. + If no token is specified, the operation is performed on the currently + authenticated token i.e. lookup-self. + +General Options: + + ` + generalOptionsUsage() + ` + +Token Lookup Options: + + -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) +} diff --git a/command/token_lookup_test.go b/command/token_lookup_test.go new file mode 100644 index 000000000..ca705d502 --- /dev/null +++ b/command/token_lookup_test.go @@ -0,0 +1,76 @@ +package command + +import ( + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/http" + "github.com/hashicorp/vault/vault" + "github.com/mitchellh/cli" + "testing" +) + +func TestTokenLookupSelf(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{ + ClientToken: token, + Ui: ui, + }, + } + + 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{ + 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", + }) + 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) + + // Verify it worked + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} \ No newline at end of file