command/auth: framework for supporting more auth methods
This commit is contained in:
parent
569991fcc5
commit
481628c41f
111
command/auth.go
111
command/auth.go
|
@ -6,21 +6,33 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/helper/flag-kv"
|
||||
"github.com/hashicorp/vault/helper/password"
|
||||
"github.com/ryanuber/columnize"
|
||||
)
|
||||
|
||||
// AuthHandler is the interface that any auth handlers must implement
|
||||
// to enable auth via the CLI.
|
||||
type AuthHandler interface {
|
||||
Auth(map[string]string) (string, error)
|
||||
Help() string
|
||||
}
|
||||
|
||||
// AuthCommand is a Command that handles authentication.
|
||||
type AuthCommand struct {
|
||||
Meta
|
||||
|
||||
Handlers map[string]AuthHandler
|
||||
}
|
||||
|
||||
func (c *AuthCommand) Run(args []string) int {
|
||||
var method string
|
||||
var methods bool
|
||||
var vars map[string]string
|
||||
flags := c.Meta.FlagSet("auth", FlagSetDefault)
|
||||
flags.BoolVar(&methods, "methods", false, "")
|
||||
flags.StringVar(&method, "method", "", "method")
|
||||
flags.Var((*kvFlag.Flag)(&vars), "var", "variables")
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
|
@ -54,36 +66,39 @@ func (c *AuthCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// token is where the final token will go
|
||||
var token string
|
||||
handler := c.Handlers[method]
|
||||
if method == "" {
|
||||
token := ""
|
||||
if len(args) > 0 {
|
||||
token = args[0]
|
||||
|
||||
// TODO(mitchellh): stdin
|
||||
} else {
|
||||
// No arguments given, read the token from user input
|
||||
fmt.Printf("Token (will be hidden): ")
|
||||
token, err = password.Read(os.Stdin)
|
||||
fmt.Printf("\n")
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error attempting to ask for token. The raw error message\n"+
|
||||
"is shown below, but the most common reason for this error is\n"+
|
||||
"that you attempted to pipe a value into auth. If you want to\n"+
|
||||
"pipe the token, please pass '-' as the token argument.\n\n"+
|
||||
"Raw error: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"A token must be passed to auth. Please view the help\n" +
|
||||
"for more information."))
|
||||
return 1
|
||||
handler = &tokenAuthHandler{Token: token}
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
methods := make([]string, 0, len(c.Handlers))
|
||||
for k, _ := range c.Handlers {
|
||||
methods = append(methods, k)
|
||||
}
|
||||
} else {
|
||||
// TODO(mitchellh): other auth methods
|
||||
sort.Strings(methods)
|
||||
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Unknown authentication method: %s\n\n"+
|
||||
"Please use a supported authentication method. The list of supported\n"+
|
||||
"authentication methods is shown below. Note that this list may not\n"+
|
||||
"be exhaustive: Vault may support other auth methods. For auth methods\n"+
|
||||
"unsupported by the CLI, please use the HTTP API.\n\n"+
|
||||
"%s",
|
||||
method,
|
||||
strings.Join(methods, ", ")))
|
||||
return 1
|
||||
}
|
||||
|
||||
token, err := handler.Auth(vars)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
// Store the token!
|
||||
|
@ -198,12 +213,52 @@ General Options:
|
|||
|
||||
Auth Options:
|
||||
|
||||
-method=name Outputs help for the authentication method with the given
|
||||
name for the remote server. If this authentication method
|
||||
is not available, exit with code 1.
|
||||
-method=name Outputs help for the authentication method with the given
|
||||
name for the remote server. If this authentication method
|
||||
is not available, exit with code 1.
|
||||
|
||||
-methods List the available auth methods.
|
||||
-methods List the available auth methods.
|
||||
|
||||
-var="key=value" Vars for the authentication method. These are determined
|
||||
on a per-method basis.
|
||||
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
// tokenAuthHandler handles retrieving the token from the command-line.
|
||||
type tokenAuthHandler struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
func (h *tokenAuthHandler) Auth(map[string]string) (string, error) {
|
||||
token := h.Token
|
||||
if token == "" {
|
||||
var err error
|
||||
|
||||
// No arguments given, read the token from user input
|
||||
fmt.Printf("Token (will be hidden): ")
|
||||
token, err = password.Read(os.Stdin)
|
||||
fmt.Printf("\n")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"Error attempting to ask for token. The raw error message\n"+
|
||||
"is shown below, but the most common reason for this error is\n"+
|
||||
"that you attempted to pipe a value into auth. If you want to\n"+
|
||||
"pipe the token, please pass '-' as the token argument.\n\n"+
|
||||
"Raw error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return "", fmt.Errorf(
|
||||
"A token must be passed to auth. Please view the help\n" +
|
||||
"for more information.")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (h *tokenAuthHandler) Help() string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package kvFlag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Flag is a flag.Value implementation for parsing user variables
|
||||
// from the command-line in the format of '-var key=value'.
|
||||
type Flag map[string]string
|
||||
|
||||
func (v *Flag) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (v *Flag) Set(raw string) error {
|
||||
idx := strings.Index(raw, "=")
|
||||
if idx == -1 {
|
||||
return fmt.Errorf("No '=' value in arg: %s", raw)
|
||||
}
|
||||
|
||||
if *v == nil {
|
||||
*v = make(map[string]string)
|
||||
}
|
||||
|
||||
key, value := raw[0:idx], raw[idx+1:]
|
||||
(*v)[key] = value
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package kvFlag
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFlag_impl(t *testing.T) {
|
||||
var _ flag.Value = new(Flag)
|
||||
}
|
||||
|
||||
func TestFlag(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Output map[string]string
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
"key=value",
|
||||
map[string]string{"key": "value"},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"key=",
|
||||
map[string]string{"key": ""},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"key=foo=bar",
|
||||
map[string]string{"key": "foo=bar"},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"key",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
f := new(Flag)
|
||||
err := f.Set(tc.Input)
|
||||
if (err != nil) != tc.Error {
|
||||
t.Fatalf("bad error. Input: %#v", tc.Input)
|
||||
}
|
||||
|
||||
actual := map[string]string(*f)
|
||||
if !reflect.DeepEqual(actual, tc.Output) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue