diff --git a/command/token_renew.go b/command/token_renew.go index 776f513569..a3404e5821 100644 --- a/command/token_renew.go +++ b/command/token_renew.go @@ -2,8 +2,8 @@ package command import ( "fmt" - "strconv" "strings" + "time" "github.com/hashicorp/vault/api" ) @@ -14,32 +14,45 @@ type TokenRenewCommand struct { } func (c *TokenRenewCommand) Run(args []string) int { - var format string + var format, increment string flags := c.Meta.FlagSet("token-renew", FlagSetDefault) flags.StringVar(&format, "format", "table", "") + flags.StringVar(&increment, "increment", "", "") flags.Usage = func() { c.Ui.Error(c.Help()) } if err := flags.Parse(args); err != nil { return 1 } args = flags.Args() - if len(args) < 1 { + if len(args) > 2 { flags.Usage() c.Ui.Error(fmt.Sprintf( - "\ntoken-renew expects at least one argument")) + "\ntoken-renew expects at most two arguments")) return 1 } - var increment int - token := args[0] - if len(args) > 1 { - value, err := strconv.ParseInt(args[1], 10, 0) + var token string + if len(args) > 0 { + token = args[0] + } + + var inc int + if len(args) == 2 { + dur, err := time.ParseDuration(args[1]) if err != nil { c.Ui.Error(fmt.Sprintf("Invalid increment: %s", err)) return 1 } - increment = int(value) + inc = int(dur / time.Second) + } else if increment != "" { + dur, err := time.ParseDuration(increment) + if err != nil { + c.Ui.Error(fmt.Sprintf("Invalid increment: %s", err)) + return 1 + } + + inc = int(dur / time.Second) } client, err := c.Client() @@ -52,10 +65,10 @@ func (c *TokenRenewCommand) Run(args []string) int { // If the given token is the same as the client's, use renew-self instead // as this is far more likely to be allowed via policy var secret *api.Secret - if client.Token() == token { - secret, err = client.Auth().Token().RenewSelf(increment) + if token == "" { + secret, err = client.Auth().Token().RenewSelf(inc) } else { - secret, err = client.Auth().Token().Renew(token, increment) + secret, err = client.Auth().Token().Renew(token, inc) } if err != nil { c.Ui.Error(fmt.Sprintf( @@ -72,17 +85,20 @@ func (c *TokenRenewCommand) Synopsis() string { func (c *TokenRenewCommand) Help() string { helpText := ` -Usage: vault token-renew [options] token [increment] +Usage: vault token-renew [options] [token] [increment] - Renew an auth token, extending the amount of time it can be used. - Token is renewable only if there is a lease associated with it. + Renew an auth token, extending the amount of time it can be used. If a token + is given to the command, '/auth/token/renew' will be called with the given + token; otherwise, '/auth/token/renew-self' will be called with the client + token. - This command is similar to "renew", but "renew" is only for lease IDs. - This command is only for tokens. + This command is similar to "renew", but "renew" is only for leases; this + command is only for tokens. - An optional increment can be given to request a certain number of - seconds to increment the lease. This request is advisory; Vault may not - adhere to it at all. + An optional increment can be given to request a certain number of seconds to + increment the lease. This request is advisory; Vault may not adhere to it at + all. If a token is being passed in on the command line, the increment can as + well; otherwise it must be passed in via the '-increment' flag. General Options: @@ -90,6 +106,11 @@ General Options: Token Renew Options: + -increment=3600 The desired increment. If not supplied, Vault will + use the default TTL. If supplied, it may still be + ignored. This can be submitted as an integer number + of seconds or a string duration (e.g. "72h"). + -format=table The format for output. By default it is a whitespace- delimited table. This can also be json or yaml. diff --git a/command/token_renew_test.go b/command/token_renew_test.go index 34ebec7e13..0dbd53d621 100644 --- a/command/token_renew_test.go +++ b/command/token_renew_test.go @@ -41,9 +41,136 @@ func TestTokenRenew(t *testing.T) { t.Fatalf("err: %s", err) } - // Verify it worked + // Renew, passing in the token args = append(args, resp.Auth.ClientToken) if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } } + +func TestTokenRenewWithIncrement(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + ui := new(cli.MockUi) + c := &TokenRenewCommand{ + Meta: Meta{ + ClientToken: token, + Ui: ui, + }, + } + + args := []string{ + "-address", addr, + } + + // Run it once for client + c.Run(args) + + // Create a token + 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) + } + + // Renew, passing in the token + args = append(args, resp.Auth.ClientToken) + args = append(args, "72h") + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + +func TestTokenRenewSelf(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + ui := new(cli.MockUi) + c := &TokenRenewCommand{ + Meta: Meta{ + ClientToken: token, + Ui: ui, + }, + } + + args := []string{ + "-address", addr, + } + + // Run it once for client + c.Run(args) + + // Create a token + 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) + } + if resp.Auth.ClientToken == "" { + t.Fatal("returned client token is empty") + } + + c.Meta.ClientToken = resp.Auth.ClientToken + + // Renew using the self endpoint + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + +func TestTokenRenewSelfWithIncrement(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := http.TestServer(t, core) + defer ln.Close() + + ui := new(cli.MockUi) + c := &TokenRenewCommand{ + Meta: Meta{ + ClientToken: token, + Ui: ui, + }, + } + + args := []string{ + "-address", addr, + } + + // Run it once for client + c.Run(args) + + // Create a token + 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) + } + if resp.Auth.ClientToken == "" { + t.Fatal("returned client token is empty") + } + + c.Meta.ClientToken = resp.Auth.ClientToken + + args = append(args, "-increment=72h") + // Renew using the self endpoint + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +}