27bb03bbc0
* adding copyright header * fix fmt and a test
172 lines
4.6 KiB
Go
172 lines
4.6 KiB
Go
// 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)
|
|
}
|