| // 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) |
| } |