Introduce auth as a subcommand

This commit is contained in:
Seth Vargo 2017-09-07 21:56:39 -04:00
parent f66d49b79b
commit 69784a3bf1
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
12 changed files with 473 additions and 954 deletions

View File

@ -1,452 +1,117 @@
package command
import (
"fmt"
"flag"
"io"
"os"
"io/ioutil"
"strings"
"github.com/hashicorp/vault/api"
"github.com/posener/complete"
"github.com/mitchellh/cli"
)
// AuthHandler is the interface that any auth handlers must implement
// to enable auth via the CLI.
type AuthHandler interface {
Auth(*api.Client, map[string]string) (*api.Secret, error)
Help() string
}
var _ cli.Command = (*AuthCommand)(nil)
// AuthCommand is a Command that handles authentication.
type AuthCommand struct {
*BaseCommand
Handlers map[string]AuthHandler
flagMethod string
flagPath string
flagNoVerify bool
flagNoStore bool
flagOnlyToken bool
// Deprecations
// TODO: remove in 0.9.0
flagTokenOnly bool
flagMethods bool
flagMethodHelp bool
Handlers map[string]LoginHandler
testStdin io.Reader // for tests
}
func (c *AuthCommand) Synopsis() string {
return "Authenticates users or machines"
return "Interact with auth methods"
}
func (c *AuthCommand) Help() string {
helpText := `
Usage: vault auth [options] [AUTH K=V...]
return strings.TrimSpace(`
Usage: vault auth <subcommand> [options] [args]
Authenticates users or machines to Vault using the provided arguments. By
default, the authentication method is "token". If not supplied via the CLI,
Vault will prompt for input. If argument is "-", the configuration options
are read from stdin.
This command groups subcommands for interacting with Vault's auth methods.
Users can list, enable, disable, and get help for different auth methods.
The -method flag allows alternative authentication providers to be used,
such as userpass, github, or cert. For these, additional "key=value" pairs
may be required. For example, to authenticate to the userpass auth backend:
To authenticate to Vault as a user or machine, use the "vault login" command
instead. This command is for interacting with the auth methods themselves, not
authenticating to Vault.
$ vault auth -method=userpass username=my-username
List all enabled auth methods:
Use "vault auth-help TYPE" for more information about the list of
configuration parameters and examples for a particular provider. Use the
"vault auth-list" command to see a list of enabled authentication providers.
$ vault auth list
If an authentication provider is mounted at a different path, the -method
flag should by the canonical type, and the -path flag should be set to the
mount path. If a github authentication provider was mounted at "github-ent",
you would authenticate to this backend like this:
Enable a new auth method "userpass";
$ vault auth -method=github -path=github-prod
$ vault auth enable userpass
If the authentication is requested with response wrapping (via -wrap-ttl),
the returned token is automatically unwrapped unless:
Get detailed help information about how to authenticate to a particular auth
method:
- The -only-token flag is used, in which case this command will output
the wrapping token
$ vault auth help github
- The -no-store flag is used, in which case this command will output
the details of the wrapping token.
For a full list of examples, please see the documentation.
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *AuthCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
f.StringVar(&StringVar{
Name: "method",
Target: &c.flagMethod,
Default: "token",
Completion: c.PredictVaultAvailableAuths(),
Usage: "Type of authentication to use such as \"userpass\" or " +
"\"ldap\". Note this corresponds to the TYPE, not the mount path. Use " +
"-path to specify the path where the authentication is mounted.",
})
f.StringVar(&StringVar{
Name: "path",
Target: &c.flagPath,
Default: "",
Completion: c.PredictVaultAuths(),
Usage: "Mount point where the auth backend is enabled. This defaults to " +
"the TYPE of backend (e.g. userpass -> userpass/).",
})
f.BoolVar(&BoolVar{
Name: "no-verify",
Target: &c.flagNoVerify,
Default: false,
Usage: "Do not verify the token after authentication. By default, Vault " +
"issue a request to get more metdata about the token. This request " +
"against the use-limit of the token. Set this to true to disable the " +
"post-authenication lookup.",
})
f.BoolVar(&BoolVar{
Name: "no-store",
Target: &c.flagNoStore,
Default: false,
Usage: "Do not persist the token to the token helper (usually the " +
"local filesystem) after authentication for use in future requests. " +
"The token will only be displayed in the command output.",
})
f.BoolVar(&BoolVar{
Name: "only-token",
Target: &c.flagOnlyToken,
Default: false,
Usage: "Output only the token with no verification. This flag is a " +
"shortcut for \"-field=token -no-store -no-verify\". Setting those " +
"flags to other values will have no affect.",
})
// Deprecations
// TODO: remove in Vault 0.9.0
f.BoolVar(&BoolVar{
Name: "token-only", // Prefer only-token
Target: &c.flagTokenOnly,
Default: false,
Hidden: true,
})
f.BoolVar(&BoolVar{
Name: "methods", // Prefer auth-list
Target: &c.flagMethods,
Default: false,
Hidden: true,
})
f.BoolVar(&BoolVar{
Name: "method-help", // Prefer auth-help
Target: &c.flagMethodHelp,
Default: false,
Hidden: true,
})
return set
}
func (c *AuthCommand) AutocompleteArgs() complete.Predictor {
return nil
}
func (c *AuthCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
Please see the individual subcommand help for detailed usage information.
`)
}
func (c *AuthCommand) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
args = f.Args()
// Deprecations - do this before any argument validations
// If we entered the run method, none of the subcommands picked up. This
// means the user is still trying to use auth as "vault auth TOKEN" or
// similar, so direct them to vault login instead.
//
// This run command is a bit messy to maintain BC for a bit. In the future,
// it will just be a tiny function, but for now we have to maintain bc.
//
// Deprecation
// TODO: remove in 0.9.0
switch {
case c.flagMethods:
c.UI.Warn(wrapAtLength(
"WARNING! The -methods flag is deprecated. Please use " +
"\"vault auth-list\". This flag will be removed in the next major " +
"release of Vault."))
cmd := &AuthListCommand{
BaseCommand: &BaseCommand{
UI: c.UI,
client: c.client,
},
}
return cmd.Run(nil)
case c.flagMethodHelp:
c.UI.Warn(wrapAtLength(
"WARNING! The -method-help flag is deprecated. Please use " +
"\"vault auth-help\". This flag will be removed in the next major " +
"release of Vault."))
cmd := &AuthHelpCommand{
BaseCommand: &BaseCommand{
UI: c.UI,
client: c.client,
},
Handlers: c.Handlers,
}
return cmd.Run([]string{c.flagMethod})
}
// TODO: remove in 0.9.0
if c.flagTokenOnly {
c.UI.Warn(wrapAtLength(
"WARNING! The -token-only flag is deprecated. Plase use -only-token " +
"instead. This flag will be removed in the next major release of " +
"Vault."))
c.flagOnlyToken = c.flagTokenOnly
}
// Parse the args for our deprecations and defer to the proper areas.
for _, arg := range args {
switch {
case strings.HasPrefix(arg, "-methods"):
c.UI.Warn(wrapAtLength(
"WARNING! The -methods flag is deprecated. Please use "+
"\"vault auth list\" instead. This flag will be removed in the "+
"next major release of Vault.") + "\n")
return (&AuthListCommand{
BaseCommand: &BaseCommand{
UI: c.UI,
client: c.client,
},
}).Run(nil)
case strings.HasPrefix(arg, "-method-help"):
c.UI.Warn(wrapAtLength(
"WARNING! The -method-help flag is deprecated. Please use "+
"\"vault auth help\" instead. This flag will be removed in the "+
"next major release of Vault.") + "\n")
// Parse the args to pull out the method, surpressing any errors because
// there could be other flags that we don't care about.
f := flag.NewFlagSet("", flag.ContinueOnError)
f.Usage = func() {}
f.SetOutput(ioutil.Discard)
flagMethod := f.String("method", "", "")
f.Parse(args)
// Set the right flags if the user requested only-token - this overrides
// any previously configured values, as documented.
if c.flagOnlyToken {
c.flagNoStore = true
c.flagNoVerify = true
c.flagField = "token"
}
// Get the auth method
authMethod := sanitizePath(c.flagMethod)
if authMethod == "" {
authMethod = "token"
}
// If no path is specified, we default the path to the backend type
// or use the plugin name if it's a plugin backend
authPath := c.flagPath
if authPath == "" {
authPath = ensureTrailingSlash(authMethod)
}
// Get the handler function
authHandler, ok := c.Handlers[authMethod]
if !ok {
c.UI.Error(wrapAtLength(fmt.Sprintf(
"Unknown authentication method: %s. Use \"vault auth-list\" to see the "+
"complete list of authentication providers. Additionally, some "+
"authentication providers are only available via the HTTP API.",
authMethod)))
return 1
}
// Pull our fake stdin if needed
stdin := (io.Reader)(os.Stdin)
if c.testStdin != nil {
stdin = c.testStdin
}
// If the user provided a token, pass it along to the auth provier.
if authMethod == "token" && len(args) == 1 {
args = []string{"token=" + args[0]}
}
config, err := parseArgsDataString(stdin, args)
if err != nil {
c.UI.Error(fmt.Sprintf("Error parsing configuration: %s", err))
return 1
}
// If the user did not specify a mount path, use the provided mount path.
if config["mount"] == "" && authPath != "" {
config["mount"] = authPath
}
// Create the client
client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
return 2
}
// Authenticate delegation to the auth handler
secret, err := authHandler.Auth(client, config)
if err != nil {
c.UI.Error(wrapAtLength(fmt.Sprintf(
"Error authenticating: %s", err)))
return 2
}
// Unset any previous token wrapping functionality. If the original request
// was for a wrapped token, we don't want future requests to be wrapped.
client.SetWrappingLookupFunc(func(string, string) string { return "" })
// Recursively extract the token, handling wrapping
unwrap := !c.flagOnlyToken && !c.flagNoStore
secret, isWrapped, err := c.extractToken(client, secret, unwrap)
if err != nil {
c.UI.Error(fmt.Sprintf("Error extracting token: %s", err))
return 2
}
if secret == nil {
c.UI.Error("Vault returned an empty secret")
return 2
}
// Handle special cases if the token was wrapped
if isWrapped {
if c.flagOnlyToken {
return PrintRawField(c.UI, secret, "wrapping_token")
}
if c.flagNoStore {
return OutputSecret(c.UI, c.flagFormat, secret)
return (&AuthHelpCommand{
BaseCommand: &BaseCommand{
UI: c.UI,
client: c.client,
},
Handlers: c.Handlers,
}).Run([]string{*flagMethod})
}
}
// If we got this far, verify we have authentication data before continuing
if secret.Auth == nil {
c.UI.Error(wrapAtLength(
"Vault returned a secret, but the secret has no authentication " +
"information attached. This should never happen and is likely a " +
"bug."))
return 2
}
// Pull the token itself out, since we don't need the rest of the auth
// information anymore/.
token := secret.Auth.ClientToken
tokenHelper, err := c.TokenHelper()
if err != nil {
c.UI.Error(fmt.Sprintf(
"Error initializing token helper: %s\n\n"+
"Please verify that the token helper is available and properly\n"+
"configured for your system. Please refer to the documentation\n"+
"on token helpers for more information.",
err))
return 1
}
if !c.flagNoVerify {
// Verify the token and pull it's list of policies
client.SetToken(token)
client.SetWrappingLookupFunc(func(string, string) string { return "" })
secret, err = client.Auth().Token().LookupSelf()
if err != nil {
c.UI.Error(fmt.Sprintf("Error verifying token: %s", err))
return 2
}
if secret == nil {
c.UI.Error("Empty response from lookup-self")
return 2
}
}
if !c.flagNoStore {
// Store the token in the local client
if err := tokenHelper.Store(token); err != nil {
c.UI.Error(fmt.Sprintf("Error storing token: %s", err))
c.UI.Error(wrapAtLength(
"Authentication was successful, but the token was not persisted. The " +
"resulting token is shown below for your records."))
OutputSecret(c.UI, c.flagFormat, secret)
return 2
}
// Warn if the VAULT_TOKEN environment variable is set, as that will take
// precedence. Don't output on token-only since we're likely piping output.
if c.flagField == "" && c.flagFormat == "table" {
if os.Getenv("VAULT_TOKEN") != "" {
c.UI.Warn(wrapAtLength("WARNING! The VAULT_TOKEN environment variable " +
"is set! This takes precedence over the value set by this command. To " +
"use the value set by this command, unset the VAULT_TOKEN environment " +
"variable or set it to the token displayed below."))
}
}
}
// If the user requested a particular field, print that out now since we
// are likely piping to another process.
if c.flagField != "" {
return PrintRawField(c.UI, secret, c.flagField)
}
// Output the secret as json or yaml if requested. We have to maintain
// backwards compatiability
if c.flagFormat != "table" {
return OutputSecret(c.UI, c.flagFormat, secret)
}
output := "Success! You are now authenticated. "
if c.flagNoVerify {
output += "The token was not verified for validity. "
}
if c.flagNoStore {
output += "The token was not stored in the token helper. "
} else {
output += "The token information displayed below is already stored in " +
"the token helper. You do NOT need to run \"vault auth\" again."
}
c.UI.Output(wrapAtLength(output) + "\n")
// TODO make this consistent with other printed token secrets.
c.UI.Output(fmt.Sprintf("token: %s", secret.TokenID()))
c.UI.Output(fmt.Sprintf("accessor: %s", secret.TokenAccessor()))
if ttl := secret.TokenTTL(); ttl != 0 {
c.UI.Output(fmt.Sprintf("duration: %s", ttl))
}
c.UI.Output(fmt.Sprintf("renewable: %t", secret.TokenIsRenewable()))
if policies := secret.TokenPolicies(); len(policies) > 0 {
c.UI.Output(fmt.Sprintf("policies: %s", policies))
}
return 0
}
// extractToken extracts the token from the given secret, automatically
// unwrapping responses and handling error conditions if unwrap is true. The
// result also returns whether it was a wrapped resonse that was not unwrapped.
func (c *AuthCommand) extractToken(client *api.Client, secret *api.Secret, unwrap bool) (*api.Secret, bool, error) {
switch {
case secret == nil:
return nil, false, fmt.Errorf("empty response from auth helper")
case secret.Auth != nil:
return secret, false, nil
case secret.WrapInfo != nil:
if secret.WrapInfo.WrappedAccessor == "" {
return nil, false, fmt.Errorf("wrapped response does not contain a token")
}
if !unwrap {
return secret, true, nil
}
client.SetToken(secret.WrapInfo.Token)
secret, err := client.Logical().Unwrap("")
if err != nil {
return nil, false, err
}
return c.extractToken(client, secret, unwrap)
default:
return nil, false, fmt.Errorf("no auth or wrapping info in response")
}
// If we got this far, we have an arg or a series of args that should be
// passed directly to the new "vault login" command.
c.UI.Warn(wrapAtLength(
"WARNING! The \"vault auth ARG\" command is deprecated and is now a "+
"subcommand for interacting with auth methods. To "+
"authenticate locally to Vault, use \"vault login\" instead. This "+
"backwards compatability will be removed in the next major release of "+
"Vault.") + "\n")
return (&LoginCommand{
BaseCommand: &BaseCommand{
UI: c.UI,
client: c.client,
},
Handlers: c.Handlers,
}).Run(args)
}

View File

@ -8,35 +8,31 @@ import (
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*AuthDisableCommand)(nil)
var _ cli.CommandAutocomplete = (*AuthDisableCommand)(nil)
// AuthDisableCommand is a Command that enables a new endpoint.
type AuthDisableCommand struct {
*BaseCommand
}
func (c *AuthDisableCommand) Synopsis() string {
return "Disables an auth provider"
return "Disables an auth method"
}
func (c *AuthDisableCommand) Help() string {
helpText := `
Usage: vault auth-disable [options] PATH
Usage: vault auth disable [options] PATH
Disables an existing authentication provider at the given PATH. The argument
corresponds to the PATH of the mount, not the TYPE!. Once the auth provider
is disabled its path can no longer be used to authenticate. All access tokens
generated via the disabled auth provider are revoked.
Disables an existing auth method at the given PATH. The argument corresponds
to the PATH of the mount, not the TYPE!. Once the auth method is disabled its
path can no longer be used to authenticate.
This command will block until all tokens are revoked.
All access tokens generated via the disabled auth method are immediately
revoked. This command will block until all tokens are revoked.
Disable the authentication provider at userpass/:
Disable the auth method at userpass/:
$ vault auth-disable userpass
For a full list of examples, please see the documentation.
$ vault auth disable userpass/
` + c.Flags().Help()
@ -82,10 +78,10 @@ func (c *AuthDisableCommand) Run(args []string) int {
}
if err := client.Sys().DisableAuth(path); err != nil {
c.UI.Error(fmt.Sprintf("Error disabling auth at %s: %s", path, err))
c.UI.Error(fmt.Sprintf("Error disabling auth method at %s: %s", path, err))
return 2
}
c.UI.Output(fmt.Sprintf("Success! Disabled the auth provider (if it existed) at: %s", path))
c.UI.Output(fmt.Sprintf("Success! Disabled the auth method (if it existed) at: %s", path))
return 0
}

View File

@ -85,7 +85,7 @@ func TestAuthDisableCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Disabled the auth provider"
expected := "Success! Disabled the auth method"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
@ -117,7 +117,7 @@ func TestAuthDisableCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error disabling auth at my-auth/: "
expected := "Error disabling auth method at my-auth/: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)

View File

@ -9,11 +9,9 @@ import (
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*AuthEnableCommand)(nil)
var _ cli.CommandAutocomplete = (*AuthEnableCommand)(nil)
// AuthEnableCommand is a Command that enables a new endpoint.
type AuthEnableCommand struct {
*BaseCommand
@ -24,30 +22,28 @@ type AuthEnableCommand struct {
}
func (c *AuthEnableCommand) Synopsis() string {
return "Enables a new auth provider"
return "Enables a new auth method"
}
func (c *AuthEnableCommand) Help() string {
helpText := `
Usage: vault auth-enable [options] TYPE
Usage: vault auth enable [options] TYPE
Enables a new authentication provider. An authentication provider is
responsible for authenticating users or machiens and assigning them
policies with which they can access Vault.
Enables a new auth method. An auth method is responsible for authenticating
users or machines and assigning them policies with which they can access
Vault.
Enable the userpass auth provider at userpass/:
Enable the userpass auth method at userpass/:
$ vault auth-enable userpass
$ vault auth enable userpass
Enable the LDAP auth provider at auth-prod/:
Enable the LDAP auth method at auth-prod/:
$ vault auth-enable -path=auth-prod ldap
$ vault auth enable -path=auth-prod ldap
Enable a custom auth plugin (after it is registered in the plugin registry):
Enable a custom auth plugin (after it's registered in the plugin registry):
$ vault auth-enable -path=my-auth -plugin-name=my-auth-plugin plugin
For a full list of examples, please see the documentation.
$ vault auth enable -path=my-auth -plugin-name=my-auth-plugin plugin
` + c.Flags().Help()
@ -64,7 +60,7 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Target: &c.flagDescription,
Completion: complete.PredictAnything,
Usage: "Human-friendly description for the purpose of this " +
"authentication provider.",
"auth method.",
})
f.StringVar(&StringVar{
@ -72,16 +68,17 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Target: &c.flagPath,
Default: "", // The default is complex, so we have to manually document
Completion: complete.PredictAnything,
Usage: "Place where the auth provider will be accessible. This must be " +
"unique across all auth providers. This defaults to the \"type\" of " +
"the mount. The auth provider will be accessible at \"/auth/<path>\".",
Usage: "Place where the auth method will be accessible. This must be " +
"unique across all auth methods. This defaults to the \"type\" of " +
"the auth method. The auth method will be accessible at " +
"\"/auth/<path>\".",
})
f.StringVar(&StringVar{
Name: "plugin-name",
Target: &c.flagPluginName,
Completion: complete.PredictAnything,
Usage: "Name of the auth provider plugin. This plugin name must already " +
Usage: "Name of the auth method plugin. This plugin name must already " +
"exist in the Vault server's plugin catalog.",
})
@ -89,7 +86,7 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Name: "local",
Target: &c.flagLocal,
Default: false,
Usage: "Mark the auth provider as local-only. Local auth providers are " +
Usage: "Mark the auth method as local-only. Local auth methods are " +
"not replicated nor removed by replication.",
})
@ -156,7 +153,7 @@ func (c *AuthEnableCommand) Run(args []string) int {
return 2
}
authThing := authType + " auth provider"
authThing := authType + " auth method"
if authType == "plugin" {
authThing = c.flagPluginName + " plugin"
}

View File

@ -89,7 +89,7 @@ func TestAuthEnableCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Enabled userpass auth provider at:"
expected := "Success! Enabled userpass auth method at:"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)

View File

@ -8,42 +8,41 @@ import (
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*AuthHelpCommand)(nil)
var _ cli.CommandAutocomplete = (*AuthHelpCommand)(nil)
// AuthHelpCommand is a Command that prints help output for a given auth
// provider
type AuthHelpCommand struct {
*BaseCommand
Handlers map[string]AuthHandler
Handlers map[string]LoginHandler
}
func (c *AuthHelpCommand) Synopsis() string {
return "Prints usage for an auth provider"
return "Prints usage for an auth method"
}
func (c *AuthHelpCommand) Help() string {
helpText := `
Usage: vault path-help [options] TYPE | PATH
Usage: vault auth help [options] TYPE | PATH
Prints usage and help for an authentication provider. If provided a TYPE,
this command retrieves the default help for the given authentication
provider of that type. If given a PATH, this command returns the help
output for the authentication provider mounted at that path. If given a
PATH argument, the path must exist and be mounted.
Prints usage and help for an auth method.
Get usage instructions for the userpass authentication provider:
- If given a TYPE, this command prints the default help for the
auth method of that type.
$ vault auth-help userpass
- If given a PATH, this command prints the help output for the
auth method enabled at that path. This path must already
exist.
Print usage for the authentication provider mounted at my-provider/
Get usage instructions for the userpass auth method:
$ vault auth-help my-provider/:
$ vault auth help userpass
Each authentication provider produces its own help output. For additional
information, please view the online documentation.
Print usage for the auth method enabled at my-method/:
$ vault auth help my-method/
Each auth method produces its own help output.
` + c.Flags().Help()
@ -98,7 +97,7 @@ func (c *AuthHelpCommand) Run(args []string) int {
// There was no auth type by that name, see if it's a mount
auths, err := client.Sys().ListAuth()
if err != nil {
c.UI.Error(fmt.Sprintf("Error listing authentication providers: %s", err))
c.UI.Error(fmt.Sprintf("Error listing auth methods: %s", err))
return 2
}
@ -106,14 +105,14 @@ func (c *AuthHelpCommand) Run(args []string) int {
auth, ok := auths[authPath]
if !ok {
c.UI.Error(fmt.Sprintf(
"Error retrieving help: unknown authentication provider: %s", args[0]))
"Error retrieving help: unknown auth method: %s", authType))
return 1
}
authHandler, ok = c.Handlers[auth.Type]
if !ok {
c.UI.Error(wrapAtLength(fmt.Sprintf(
"INTERNAL ERROR! Found an authentication provider mounted at %s, but "+
"INTERNAL ERROR! Found an auth method enabled at %s, but "+
"its type %q is not registered in Vault. This is a bug and should "+
"be reported. Please open an issue at github.com/hashicorp/vault.",
authPath, authType)))

View File

@ -17,7 +17,7 @@ func testAuthHelpCommand(tb testing.TB) (*cli.MockUi, *AuthHelpCommand) {
BaseCommand: &BaseCommand{
UI: ui,
},
Handlers: map[string]AuthHandler{
Handlers: map[string]LoginHandler{
"userpass": &credUserpass.CLIHandler{
DefaultMount: "userpass",
},
@ -88,7 +88,7 @@ func TestAuthHelpCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Usage: vault auth -method=userpass"
expected := "Usage: vault login -method=userpass"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
@ -101,7 +101,7 @@ func TestAuthHelpCommand_Run(t *testing.T) {
client, closer := testVaultServer(t)
defer closer()
// No mounted auth backends
// No mounted auth methods
ui, cmd := testAuthHelpCommand(t)
cmd.client = client
@ -113,7 +113,7 @@ func TestAuthHelpCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Usage: vault auth -method=userpass"
expected := "Usage: vault login -method=userpass"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
@ -136,7 +136,7 @@ func TestAuthHelpCommand_Run(t *testing.T) {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error listing authentication providers: "
expected := "Error listing auth methods: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)

View File

@ -11,12 +11,9 @@ import (
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*AuthListCommand)(nil)
var _ cli.CommandAutocomplete = (*AuthListCommand)(nil)
// AuthListCommand is a Command that lists the enabled authentication methods
// and data about them.
type AuthListCommand struct {
*BaseCommand
@ -24,25 +21,24 @@ type AuthListCommand struct {
}
func (c *AuthListCommand) Synopsis() string {
return "Lists enabled auth providers"
return "Lists enabled auth methods"
}
func (c *AuthListCommand) Help() string {
helpText := `
Usage: vault auth-methods [options]
Usage: vault auth list [options]
Lists the enabled authentication providers on the Vault server. This command
also outputs information about the provider including configuration and
human-friendly descriptions. A TTL of "system" indicates that the system
default is in use.
Lists the enabled auth methods on the Vault server. This command also outputs
information about the method including configuration and human-friendly
descriptions. A TTL of "system" indicates that the system default is in use.
List all enabled authentication providers:
List all enabled auth methods:
$ vault auth-list
$ vault auth list
List all enabled authentication providers with detailed output:
List all enabled auth methods with detailed output:
$ vault auth-list -detailed
$ vault auth list -detailed
` + c.Flags().Help()
@ -59,7 +55,7 @@ func (c *AuthListCommand) Flags() *FlagSets {
Target: &c.flagDetailed,
Default: false,
Usage: "Print detailed information such as configuration and replication " +
"status about each authentication provider.",
"status about each auth method.",
})
return set
@ -100,11 +96,11 @@ func (c *AuthListCommand) Run(args []string) int {
}
if c.flagDetailed {
c.UI.Output(tableOutput(c.detailedMounts(auths)))
c.UI.Output(tableOutput(c.detailedMounts(auths), nil))
return 0
}
c.UI.Output(tableOutput(c.simpleMounts(auths)))
c.UI.Output(tableOutput(c.simpleMounts(auths), nil))
return 0
}

View File

@ -6,7 +6,6 @@ import (
"github.com/mitchellh/cli"
"github.com/hashicorp/vault/api"
credToken "github.com/hashicorp/vault/builtin/credential/token"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
"github.com/hashicorp/vault/command/token"
@ -23,7 +22,7 @@ func testAuthCommand(tb testing.TB) (*cli.MockUi, *AuthCommand) {
// Override to our own token helper
tokenHelper: token.NewTestingTokenHelper(),
},
Handlers: map[string]AuthHandler{
Handlers: map[string]LoginHandler{
"token": &credToken.CLIHandler{},
"userpass": &credUserpass.CLIHandler{},
},
@ -33,55 +32,61 @@ func testAuthCommand(tb testing.TB) (*cli.MockUi, *AuthCommand) {
func TestAuthCommand_Run(t *testing.T) {
t.Parallel()
deprecations := []struct {
name string
args []string
out string
code int
}{
{
"methods",
[]string{"-methods"},
"token/",
0,
},
{
"method_help",
[]string{"-method", "userpass", "-method-help"},
"Usage: vault auth -method=userpass",
0,
},
}
t.Run("deprecations", func(t *testing.T) {
// TODO: remove in 0.9.0
t.Run("deprecated_methods", func(t *testing.T) {
t.Parallel()
for _, tc := range deprecations {
tc := tc
client, closer := testVaultServer(t)
defer closer()
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
ui, cmd := testAuthCommand(t)
cmd.client = client
client, closer := testVaultServer(t)
defer closer()
// vault auth -methods -> vault auth list
code := cmd.Run([]string{"-methods"})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
}
stdout, stderr := ui.OutputWriter.String(), ui.ErrorWriter.String()
ui, cmd := testAuthCommand(t)
cmd.client = client
if expected := "WARNING!"; !strings.Contains(stderr, expected) {
t.Errorf("expected %q to contain %q", stderr, expected)
}
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 expected := "token/"; !strings.Contains(stdout, expected) {
t.Errorf("expected %q to contain %q", stdout, expected)
}
})
t.Run("custom_path", func(t *testing.T) {
t.Run("deprecated_method_help", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
ui, cmd := testAuthCommand(t)
cmd.client = client
// vault auth -method=foo -method-help -> vault auth help foo
code := cmd.Run([]string{
"-method=userpass",
"-method-help",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
}
stdout, stderr := ui.OutputWriter.String(), ui.ErrorWriter.String()
if expected := "WARNING!"; !strings.Contains(stderr, expected) {
t.Errorf("expected %q to contain %q", stderr, expected)
}
if expected := "vault login"; !strings.Contains(stdout, expected) {
t.Errorf("expected %q to contain %q", stdout, expected)
}
})
t.Run("deprecated_login", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
@ -100,11 +105,7 @@ func TestAuthCommand_Run(t *testing.T) {
ui, cmd := testAuthCommand(t)
cmd.client = client
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
// vault auth ARGS -> vault login ARGS
code := cmd.Run([]string{
"-method", "userpass",
"-path", "my-auth",
@ -112,420 +113,16 @@ func TestAuthCommand_Run(t *testing.T) {
"password=test",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
}
stdout, stderr := ui.OutputWriter.String(), ui.ErrorWriter.String()
if expected := "WARNING!"; !strings.Contains(stderr, expected) {
t.Errorf("expected %q to contain %q", stderr, expected)
}
expected := "Success! You are now authenticated."
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to be %q", combined, expected)
}
storedToken, err := tokenHelper.Get()
if err != nil {
t.Fatal(err)
}
if l, exp := len(storedToken), 36; l != exp {
t.Errorf("expected token to be %d characters, was %d: %q", exp, l, storedToken)
}
})
t.Run("no_verify", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"default"},
TTL: "30m",
NumUses: 1,
})
if err != nil {
t.Fatal(err)
}
token := secret.Auth.ClientToken
_, cmd := testAuthCommand(t)
cmd.client = client
code := cmd.Run([]string{
"-no-verify",
token,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
lookup, err := client.Auth().Token().Lookup(token)
if err != nil {
t.Fatal(err)
}
// There was 1 use to start, make sure we didn't use it (verifying would
// use it).
uses := lookup.TokenRemainingUses()
if uses != 1 {
t.Errorf("expected %d to be %d", uses, 1)
}
})
t.Run("no_store", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"default"},
TTL: "30m",
})
if err != nil {
t.Fatal(err)
}
token := secret.Auth.ClientToken
_, cmd := testAuthCommand(t)
cmd.client = client
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
// Ensure we have no token to start
if storedToken, err := tokenHelper.Get(); err != nil || storedToken != "" {
t.Errorf("expected token helper to be empty: %s: %q", err, storedToken)
}
code := cmd.Run([]string{
"-no-store",
token,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
storedToken, err := tokenHelper.Get()
if err != nil {
t.Fatal(err)
}
if exp := ""; storedToken != exp {
t.Errorf("expected %q to be %q", storedToken, exp)
}
})
t.Run("stores", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"default"},
TTL: "30m",
})
if err != nil {
t.Fatal(err)
}
token := secret.Auth.ClientToken
_, cmd := testAuthCommand(t)
cmd.client = client
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
code := cmd.Run([]string{
token,
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
storedToken, err := tokenHelper.Get()
if err != nil {
t.Fatal(err)
}
if storedToken != token {
t.Errorf("expected %q to be %q", storedToken, token)
}
})
t.Run("only_token", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
"password": "test",
"policies": "default",
}); err != nil {
t.Fatal(err)
}
ui, cmd := testAuthCommand(t)
cmd.client = client
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
code := cmd.Run([]string{
"-only-token",
"-method", "userpass",
"username=test",
"password=test",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
// Verify only the token was printed
token := ui.OutputWriter.String() + ui.ErrorWriter.String()
if l, exp := len(token), 36; l != exp {
t.Errorf("expected token to be %d characters, was %d: %q", exp, l, token)
}
// Verify the token was not stored
if storedToken, err := tokenHelper.Get(); err != nil || storedToken != "" {
t.Fatalf("expted token to not be stored: %s: %q", err, storedToken)
}
})
t.Run("failure_no_store", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
ui, cmd := testAuthCommand(t)
cmd.client = client
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
code := cmd.Run([]string{
"not-a-real-token",
})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error verifying token: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
if storedToken, err := tokenHelper.Get(); err != nil || storedToken != "" {
t.Fatalf("expected token to not be stored: %s: %q", err, storedToken)
}
})
t.Run("wrap_auto_unwrap", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
"password": "test",
"policies": "default",
}); err != nil {
t.Fatal(err)
}
_, cmd := testAuthCommand(t)
cmd.client = client
// Set the wrapping ttl to 5s. We can't set this via the flag because we
// override the client object before that particular flag is parsed.
client.SetWrappingLookupFunc(func(string, string) string { return "5m" })
code := cmd.Run([]string{
"-method", "userpass",
"username=test",
"password=test",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
// Unset the wrapping
client.SetWrappingLookupFunc(func(string, string) string { return "" })
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
token, err := tokenHelper.Get()
if err != nil || token == "" {
t.Fatalf("expected token from helper: %s: %q", err, token)
}
// Ensure the resulting token is unwrapped
secret, err := client.Auth().Token().LookupSelf()
if err != nil {
t.Error(err)
}
if secret.WrapInfo != nil {
t.Errorf("expected to be unwrapped: %#v", secret)
}
})
t.Run("wrap_only_token", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
"password": "test",
"policies": "default",
}); err != nil {
t.Fatal(err)
}
ui, cmd := testAuthCommand(t)
cmd.client = client
// Set the wrapping ttl to 5s. We can't set this via the flag because we
// override the client object before that particular flag is parsed.
client.SetWrappingLookupFunc(func(string, string) string { return "5m" })
code := cmd.Run([]string{
"-only-token",
"-method", "userpass",
"username=test",
"password=test",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
// Unset the wrapping
client.SetWrappingLookupFunc(func(string, string) string { return "" })
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
storedToken, err := tokenHelper.Get()
if err != nil || storedToken != "" {
t.Fatalf("expected token to not be stored: %s: %q", err, storedToken)
}
token := ui.OutputWriter.String()
if token == "" {
t.Errorf("expected %q to not be %q", token, "")
}
if strings.Contains(token, "\n") {
t.Errorf("expected %q to not contain %q", token, "\n")
}
// Ensure the resulting token is, in fact, still wrapped.
client.SetToken(token)
secret, err := client.Logical().Unwrap("")
if err != nil {
t.Error(err)
}
if secret == nil || secret.Auth == nil || secret.Auth.ClientToken == "" {
t.Fatalf("expected secret to have auth: %#v", secret)
}
})
t.Run("wrap_no_store", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{
"password": "test",
"policies": "default",
}); err != nil {
t.Fatal(err)
}
ui, cmd := testAuthCommand(t)
cmd.client = client
// Set the wrapping ttl to 5s. We can't set this via the flag because we
// override the client object before that particular flag is parsed.
client.SetWrappingLookupFunc(func(string, string) string { return "5m" })
code := cmd.Run([]string{
"-no-store",
"-method", "userpass",
"username=test",
"password=test",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
// Unset the wrapping
client.SetWrappingLookupFunc(func(string, string) string { return "" })
tokenHelper, err := cmd.TokenHelper()
if err != nil {
t.Fatal(err)
}
storedToken, err := tokenHelper.Get()
if err != nil || storedToken != "" {
t.Fatalf("expected token to not be stored: %s: %q", err, storedToken)
}
expected := "wrapping_token"
output := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(output, expected) {
t.Errorf("expected %q to contain %q", output, expected)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServerBad(t)
defer closer()
ui, cmd := testAuthCommand(t)
cmd.client = client
code := cmd.Run([]string{
"token",
})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error verifying token: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
if expected := "Success! You are now authenticated."; !strings.Contains(stdout, expected) {
t.Errorf("expected %q to contain %q", stdout, expected)
}
})

120
command/auth_tune.go Normal file
View File

@ -0,0 +1,120 @@
package command
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
var _ cli.Command = (*AuthTuneCommand)(nil)
var _ cli.CommandAutocomplete = (*AuthTuneCommand)(nil)
type AuthTuneCommand struct {
*BaseCommand
flagDefaultLeaseTTL time.Duration
flagMaxLeaseTTL time.Duration
}
func (c *AuthTuneCommand) Synopsis() string {
return "Tunes an auth method configuration"
}
func (c *AuthTuneCommand) Help() string {
helpText := `
Usage: vault auth tune [options] PATH
Tunes the configuration options for the auth method at the given PATH. The
argument corresponds to the PATH where the auth method is enabled, not the
TYPE!
Tune the default lease for the github auth method:
$ vault auth tune -default-lease-ttl=72h github/
` + c.Flags().Help()
return strings.TrimSpace(helpText)
}
func (c *AuthTuneCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP)
f := set.NewFlagSet("Command Options")
f.DurationVar(&DurationVar{
Name: "default-lease-ttl",
Target: &c.flagDefaultLeaseTTL,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: "The default lease TTL for this auth method. If unspecified, this " +
"defaults to the Vault server's globally configured default lease TTL, " +
"or a previously configured value for the auth method.",
})
f.DurationVar(&DurationVar{
Name: "max-lease-ttl",
Target: &c.flagMaxLeaseTTL,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: "The maximum lease TTL for this auth method. If unspecified, this " +
"defaults to the Vault server's globally configured maximum lease TTL, " +
"or a previously configured value for the auth method.",
})
return set
}
func (c *AuthTuneCommand) AutocompleteArgs() complete.Predictor {
return c.PredictVaultAuths()
}
func (c *AuthTuneCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *AuthTuneCommand) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
args = f.Args()
switch {
case len(args) < 1:
c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args)))
return 1
case len(args) > 1:
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1, got %d)", len(args)))
return 1
}
client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
return 2
}
// Append /auth (since that's where auths live) and a trailing slash to
// indicate it's a path in output
mountPath := ensureTrailingSlash(sanitizePath(args[0]))
if err := client.Sys().TuneMount("/auth/"+mountPath, api.MountConfigInput{
DefaultLeaseTTL: ttlToAPI(c.flagDefaultLeaseTTL),
MaxLeaseTTL: ttlToAPI(c.flagMaxLeaseTTL),
}); err != nil {
c.UI.Error(fmt.Sprintf("Error tuning auth method %s: %s", mountPath, err))
return 2
}
c.UI.Output(fmt.Sprintf("Success! Tuned the auth method at: %s", mountPath))
return 0
}

149
command/auth_tune_test.go Normal file
View File

@ -0,0 +1,149 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
)
func testAuthTuneCommand(tb testing.TB) (*cli.MockUi, *AuthTuneCommand) {
tb.Helper()
ui := cli.NewMockUi()
return ui, &AuthTuneCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
}
func TestAuthTuneCommand_Run(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
out string
code int
}{
{
"not_enough_args",
[]string{},
"Not enough arguments",
1,
},
{
"too_many_args",
[]string{"foo", "bar"},
"Too many arguments",
1,
},
}
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()
ui, cmd := testAuthTuneCommand(t)
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)
}
})
}
})
t.Run("integration", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
ui, cmd := testAuthTuneCommand(t)
cmd.client = client
// Mount
if err := client.Sys().EnableAuthWithOptions("my-auth", &api.EnableAuthOptions{
Type: "userpass",
}); err != nil {
t.Fatal(err)
}
code := cmd.Run([]string{
"-default-lease-ttl", "30m",
"-max-lease-ttl", "1h",
"my-auth/",
})
if exp := 0; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Success! Tuned the auth method at: my-auth/"
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
auths, err := client.Sys().ListAuth()
if err != nil {
t.Fatal(err)
}
mountInfo, ok := auths["my-auth/"]
if !ok {
t.Fatalf("expected auth to exist")
}
if exp := "userpass"; mountInfo.Type != exp {
t.Errorf("expected %q to be %q", mountInfo.Type, exp)
}
if exp := 1800; mountInfo.Config.DefaultLeaseTTL != exp {
t.Errorf("expected %d to be %d", mountInfo.Config.DefaultLeaseTTL, exp)
}
if exp := 3600; mountInfo.Config.MaxLeaseTTL != exp {
t.Errorf("expected %d to be %d", mountInfo.Config.MaxLeaseTTL, exp)
}
})
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()
client, closer := testVaultServerBad(t)
defer closer()
ui, cmd := testAuthTuneCommand(t)
cmd.client = client
code := cmd.Run([]string{
"userpass/",
})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error tuning auth method userpass/: "
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 := testAuthTuneCommand(t)
assertNoTabs(t, cmd)
})
}

View File

@ -3,7 +3,7 @@ package token
// TokenHelper is an interface that contains basic operations that must be
// implemented by a token helper
type TokenHelper interface {
// Path displays a backend-specific path; for the internal helper this
// Path displays a method-specific path; for the internal helper this
// is the location of the token stored on disk; for the external helper
// this is the location of the binary being invoked
Path() string