blob: 853d6eade7c4531c291cba85c2159ad4b158ffbe [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package token
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/hashicorp/go-secure-stdlib/password"
"github.com/hashicorp/vault/api"
)
type CLIHandler struct {
// for tests
testStdin io.Reader
testStdout io.Writer
}
func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, error) {
// Parse "lookup" first - we want to return an early error if the user
// supplied an invalid value here before we prompt them for a token. It would
// be annoying to type your token and then be told you supplied an invalid
// value that we could have known in advance.
lookup := true
if x, ok := m["lookup"]; ok {
parsed, err := strconv.ParseBool(x)
if err != nil {
return nil, fmt.Errorf("Failed to parse \"lookup\" as boolean: %w", err)
}
lookup = parsed
}
// Parse the token.
token, ok := m["token"]
if !ok {
// Override the output
stdout := h.testStdout
if stdout == nil {
stdout = os.Stderr
}
// No arguments given, read the token from user input
fmt.Fprintf(stdout, "Token (will be hidden): ")
var err error
token, err = password.Read(os.Stdin)
fmt.Fprintf(stdout, "\n")
if err != nil {
if err == password.ErrInterrupted {
return nil, fmt.Errorf("user interrupted")
}
return nil, fmt.Errorf("An error occurred attempting to "+
"ask for a token. The raw error message is shown below, but usually "+
"this is because you attempted to pipe a value into the command or "+
"you are executing outside of a terminal (tty). If you want to pipe "+
"the value, pass \"-\" as the argument to read from stdin. The raw "+
"error was: %w", err)
}
}
// Remove any whitespace, etc.
token = strings.TrimSpace(token)
if token == "" {
return nil, fmt.Errorf(
"a token must be passed to auth, please view the help for more " +
"information")
}
// If the user declined verification, return now. Note that we will not have
// a lot of information about the token.
if !lookup {
return &api.Secret{
Auth: &api.SecretAuth{
ClientToken: token,
},
}, nil
}
// If we got this far, we want to lookup and lookup the token and pull it's
// list of policies an metadata.
c.SetToken(token)
c.SetWrappingLookupFunc(func(string, string) string { return "" })
secret, err := c.Auth().Token().LookupSelf()
if err != nil {
return nil, fmt.Errorf("error looking up token: %w", err)
}
if secret == nil {
return nil, fmt.Errorf("empty response from lookup-self")
}
// Return an auth struct that "looks" like the response from an auth method.
// lookup and lookup-self return their data in data, not auth. We try to
// mirror that data here.
id, err := secret.TokenID()
if err != nil {
return nil, fmt.Errorf("error accessing token ID: %w", err)
}
accessor, err := secret.TokenAccessor()
if err != nil {
return nil, fmt.Errorf("error accessing token accessor: %w", err)
}
// This populates secret.Auth
_, err = secret.TokenPolicies()
if err != nil {
return nil, fmt.Errorf("error accessing token policies: %w", err)
}
metadata, err := secret.TokenMetadata()
if err != nil {
return nil, fmt.Errorf("error accessing token metadata: %w", err)
}
dur, err := secret.TokenTTL()
if err != nil {
return nil, fmt.Errorf("error converting token TTL: %w", err)
}
renewable, err := secret.TokenIsRenewable()
if err != nil {
return nil, fmt.Errorf("error checking if token is renewable: %w", err)
}
return &api.Secret{
Auth: &api.SecretAuth{
ClientToken: id,
Accessor: accessor,
Policies: secret.Auth.Policies,
TokenPolicies: secret.Auth.TokenPolicies,
IdentityPolicies: secret.Auth.IdentityPolicies,
Metadata: metadata,
LeaseDuration: int(dur.Seconds()),
Renewable: renewable,
},
}, nil
}
func (h *CLIHandler) Help() string {
help := `
Usage: vault login TOKEN [CONFIG K=V...]
The token auth method allows logging in directly with a token. This
can be a token from the "token-create" command or API. There are no
configuration options for this auth method.
Authenticate using a token:
$ vault login 96ddf4bc-d217-f3ba-f9bd-017055595017
Authenticate but do not lookup information about the token:
$ vault login token=96ddf4bc-d217-f3ba-f9bd-017055595017 lookup=false
This token usually comes from a different source such as the API or via the
built-in "vault token create" command.
Configuration:
token=<string>
The token to use for authentication. This is usually provided directly
via the "vault login" command.
lookup=<bool>
Perform a lookup of the token's metadata and policies.
`
return strings.TrimSpace(help)
}