Introduce auth as a subcommand
This commit is contained in:
parent
f66d49b79b
commit
69784a3bf1
491
command/auth.go
491
command/auth.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue