2023-03-15 16:00:52 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-08-30 16:48:54 +00:00
|
|
|
"io/ioutil"
|
2018-07-12 22:38:18 +00:00
|
|
|
"os"
|
2017-08-28 20:44:35 +00:00
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
|
|
"github.com/hashicorp/vault/command/token"
|
2018-08-10 16:13:06 +00:00
|
|
|
"github.com/hashicorp/vault/helper/namespace"
|
2022-03-14 19:54:41 +00:00
|
|
|
"github.com/mattn/go-isatty"
|
2017-08-28 20:44:35 +00:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/posener/complete"
|
|
|
|
)
|
|
|
|
|
2018-08-22 18:37:40 +00:00
|
|
|
const (
|
|
|
|
// maxLineLength is the maximum width of any line.
|
|
|
|
maxLineLength int = 78
|
|
|
|
|
2018-09-05 19:52:54 +00:00
|
|
|
// notSetValue is a flag value for a not-set value
|
|
|
|
notSetValue = "(not set)"
|
2018-08-22 18:37:40 +00:00
|
|
|
)
|
2017-08-28 20:44:35 +00:00
|
|
|
|
|
|
|
// reRemoveWhitespace is a regular expression for stripping whitespace from
|
|
|
|
// a string.
|
|
|
|
var reRemoveWhitespace = regexp.MustCompile(`[\s]+`)
|
|
|
|
|
|
|
|
type BaseCommand struct {
|
|
|
|
UI cli.Ui
|
|
|
|
|
|
|
|
flags *FlagSets
|
|
|
|
flagsOnce sync.Once
|
|
|
|
|
2023-05-17 13:38:34 +00:00
|
|
|
flagAddress string
|
|
|
|
flagAgentProxyAddress string
|
|
|
|
flagCACert string
|
|
|
|
flagCAPath string
|
|
|
|
flagClientCert string
|
|
|
|
flagClientKey string
|
|
|
|
flagNamespace string
|
|
|
|
flagNS string
|
|
|
|
flagPolicyOverride bool
|
|
|
|
flagTLSServerName string
|
|
|
|
flagTLSSkipVerify bool
|
|
|
|
flagDisableRedirects bool
|
|
|
|
flagWrapTTL time.Duration
|
|
|
|
flagUnlockKey string
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2019-02-01 22:13:51 +00:00
|
|
|
flagFormat string
|
|
|
|
flagField string
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
flagDetailed bool
|
2019-02-01 22:13:51 +00:00
|
|
|
flagOutputCurlString bool
|
2022-04-27 23:35:18 +00:00
|
|
|
flagOutputPolicy bool
|
2022-02-24 20:16:15 +00:00
|
|
|
flagNonInteractive bool
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2018-03-30 16:11:10 +00:00
|
|
|
flagMFA []string
|
|
|
|
|
2021-10-27 15:04:04 +00:00
|
|
|
flagHeader map[string]string
|
|
|
|
|
2017-09-05 03:53:57 +00:00
|
|
|
tokenHelper token.TokenHelper
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
client *api.Client
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Client returns the HTTP API client. The client is cached on the command to
|
|
|
|
// save performance on future calls.
|
|
|
|
func (c *BaseCommand) Client() (*api.Client, error) {
|
2017-08-29 04:24:22 +00:00
|
|
|
// Read the test client if present
|
|
|
|
if c.client != nil {
|
|
|
|
return c.client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
config := api.DefaultConfig()
|
|
|
|
|
|
|
|
if err := config.ReadEnvironment(); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to read environment")
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.flagAddress != "" {
|
|
|
|
config.Address = c.flagAddress
|
|
|
|
}
|
2023-05-17 13:38:34 +00:00
|
|
|
if c.flagAgentProxyAddress != "" {
|
|
|
|
config.Address = c.flagAgentProxyAddress
|
2019-02-15 01:10:36 +00:00
|
|
|
}
|
2017-08-29 04:24:22 +00:00
|
|
|
|
2019-02-01 22:13:51 +00:00
|
|
|
if c.flagOutputCurlString {
|
|
|
|
config.OutputCurlString = c.flagOutputCurlString
|
|
|
|
}
|
2022-04-27 23:35:18 +00:00
|
|
|
if c.flagOutputPolicy {
|
|
|
|
config.OutputPolicy = c.flagOutputPolicy
|
|
|
|
}
|
2019-02-01 22:13:51 +00:00
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
// If we need custom TLS configuration, then set it
|
|
|
|
if c.flagCACert != "" || c.flagCAPath != "" || c.flagClientCert != "" ||
|
|
|
|
c.flagClientKey != "" || c.flagTLSServerName != "" || c.flagTLSSkipVerify {
|
|
|
|
t := &api.TLSConfig{
|
|
|
|
CACert: c.flagCACert,
|
|
|
|
CAPath: c.flagCAPath,
|
|
|
|
ClientCert: c.flagClientCert,
|
|
|
|
ClientKey: c.flagClientKey,
|
|
|
|
TLSServerName: c.flagTLSServerName,
|
|
|
|
Insecure: c.flagTLSSkipVerify,
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
2019-12-18 10:22:15 +00:00
|
|
|
|
|
|
|
// Setup TLS config
|
|
|
|
if err := config.ConfigureTLS(t); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to setup TLS config")
|
|
|
|
}
|
2017-08-29 04:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build the client
|
|
|
|
client, err := api.NewClient(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create client")
|
|
|
|
}
|
|
|
|
|
2018-07-12 22:38:18 +00:00
|
|
|
// Turn off retries on the CLI
|
|
|
|
if os.Getenv(api.EnvVaultMaxRetries) == "" {
|
|
|
|
client.SetMaxRetries(0)
|
|
|
|
}
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
// Set the wrapping function
|
|
|
|
client.SetWrappingLookupFunc(c.DefaultWrappingLookupFunc)
|
|
|
|
|
|
|
|
// Get the token if it came in from the environment
|
|
|
|
token := client.Token()
|
|
|
|
|
|
|
|
// If we don't have a token, check the token helper
|
|
|
|
if token == "" {
|
2017-09-05 03:53:57 +00:00
|
|
|
helper, err := c.TokenHelper()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to get token helper")
|
|
|
|
}
|
|
|
|
token, err = helper.Get()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to get token from token helper")
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
2017-08-29 04:24:22 +00:00
|
|
|
}
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
// Set the token
|
|
|
|
if token != "" {
|
|
|
|
client.SetToken(token)
|
|
|
|
}
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2018-03-30 16:11:10 +00:00
|
|
|
client.SetMFACreds(c.flagMFA)
|
2018-08-27 16:03:39 +00:00
|
|
|
|
|
|
|
// flagNS takes precedence over flagNamespace. After resolution, point both
|
|
|
|
// flags to the same value to be able to use them interchangeably anywhere.
|
2018-09-05 19:52:54 +00:00
|
|
|
if c.flagNS != notSetValue {
|
2018-08-27 16:03:39 +00:00
|
|
|
c.flagNamespace = c.flagNS
|
|
|
|
}
|
2018-09-05 19:52:54 +00:00
|
|
|
if c.flagNamespace != notSetValue {
|
2018-08-22 18:37:40 +00:00
|
|
|
client.SetNamespace(namespace.Canonicalize(c.flagNamespace))
|
|
|
|
}
|
2018-11-20 17:33:00 +00:00
|
|
|
if c.flagPolicyOverride {
|
|
|
|
client.SetPolicyOverride(c.flagPolicyOverride)
|
|
|
|
}
|
2018-03-30 16:11:10 +00:00
|
|
|
|
2021-10-27 15:04:04 +00:00
|
|
|
if c.flagHeader != nil {
|
|
|
|
|
|
|
|
var forbiddenHeaders []string
|
|
|
|
for key, val := range c.flagHeader {
|
|
|
|
|
|
|
|
if strings.HasPrefix(key, "X-Vault-") {
|
|
|
|
forbiddenHeaders = append(forbiddenHeaders, key)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
client.AddHeader(key, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(forbiddenHeaders) > 0 {
|
|
|
|
return nil, fmt.Errorf("failed to setup Headers[%s]: Header starting by 'X-Vault-' are for internal usage only", strings.Join(forbiddenHeaders, ", "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-06 18:06:17 +00:00
|
|
|
c.client = client
|
|
|
|
|
2017-08-29 04:24:22 +00:00
|
|
|
return client, nil
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 17:14:32 +00:00
|
|
|
// SetAddress sets the token helper on the command; useful for the demo server and other outside cases.
|
|
|
|
func (c *BaseCommand) SetAddress(addr string) {
|
|
|
|
c.flagAddress = addr
|
|
|
|
}
|
|
|
|
|
2018-03-16 15:31:00 +00:00
|
|
|
// SetTokenHelper sets the token helper on the command.
|
|
|
|
func (c *BaseCommand) SetTokenHelper(th token.TokenHelper) {
|
|
|
|
c.tokenHelper = th
|
|
|
|
}
|
|
|
|
|
2017-09-05 03:53:57 +00:00
|
|
|
// TokenHelper returns the token helper attached to the command.
|
|
|
|
func (c *BaseCommand) TokenHelper() (token.TokenHelper, error) {
|
|
|
|
if c.tokenHelper != nil {
|
|
|
|
return c.tokenHelper, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
helper, err := DefaultTokenHelper()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return helper, nil
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
// DefaultWrappingLookupFunc is the default wrapping function based on the
|
|
|
|
// CLI flag.
|
|
|
|
func (c *BaseCommand) DefaultWrappingLookupFunc(operation, path string) string {
|
|
|
|
if c.flagWrapTTL != 0 {
|
|
|
|
return c.flagWrapTTL.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.DefaultWrappingLookupFunc(operation, path)
|
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
// getValidationRequired checks to see if the secret exists and has an MFA
|
|
|
|
// requirement. If MFA is required and the number of constraints is greater than
|
|
|
|
// 1, we can assert that interactive validation is not required.
|
|
|
|
func (c *BaseCommand) getMFAValidationRequired(secret *api.Secret) bool {
|
|
|
|
if secret != nil && secret.Auth != nil && secret.Auth.MFARequirement != nil {
|
|
|
|
if c.flagMFA == nil && len(secret.Auth.MFARequirement.MFAConstraints) == 1 {
|
|
|
|
return true
|
|
|
|
} else if len(secret.Auth.MFARequirement.MFAConstraints) > 1 {
|
|
|
|
return true
|
|
|
|
}
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
// getInteractiveMFAMethodInfo returns MFA method information only if operating
|
|
|
|
// in interactive mode and one MFA method is configured.
|
|
|
|
func (c *BaseCommand) getInteractiveMFAMethodInfo(secret *api.Secret) *MFAMethodInfo {
|
|
|
|
if secret == nil || secret.Auth == nil || secret.Auth.MFARequirement == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
mfaConstraints := secret.Auth.MFARequirement.MFAConstraints
|
|
|
|
if c.flagNonInteractive || len(mfaConstraints) != 1 || !isatty.IsTerminal(os.Stdin.Fd()) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, mfaConstraint := range mfaConstraints {
|
2022-03-14 19:54:41 +00:00
|
|
|
if len(mfaConstraint.Any) != 1 {
|
2022-10-26 21:02:26 +00:00
|
|
|
return nil
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
return &MFAMethodInfo{
|
2022-03-14 19:54:41 +00:00
|
|
|
methodType: mfaConstraint.Any[0].Type,
|
|
|
|
methodID: mfaConstraint.Any[0].ID,
|
|
|
|
usePasscode: mfaConstraint.Any[0].UsesPasscode,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
return nil
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
func (c *BaseCommand) validateMFA(reqID string, methodInfo MFAMethodInfo) (*api.Secret, error) {
|
2022-03-14 19:54:41 +00:00
|
|
|
var passcode string
|
|
|
|
var err error
|
|
|
|
if methodInfo.usePasscode {
|
|
|
|
passcode, err = c.UI.AskSecret(fmt.Sprintf("Enter the passphrase for methodID %q of type %q:", methodInfo.methodID, methodInfo.methodType))
|
|
|
|
if err != nil {
|
2022-10-26 21:02:26 +00:00
|
|
|
return nil, fmt.Errorf("failed to read passphrase: %w. please validate the login by sending a request to sys/mfa/validate", err)
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.UI.Warn("Asking Vault to perform MFA validation with upstream service. " +
|
|
|
|
"You should receive a push notification in your authenticator app shortly")
|
|
|
|
}
|
|
|
|
|
|
|
|
// passcode could be an empty string
|
2022-04-15 18:13:15 +00:00
|
|
|
mfaPayload := map[string]interface{}{
|
|
|
|
methodInfo.methodID: []string{passcode},
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client, err := c.Client()
|
|
|
|
if err != nil {
|
2022-10-26 21:02:26 +00:00
|
|
|
return nil, err
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-26 21:02:26 +00:00
|
|
|
return client.Sys().MFAValidate(reqID, mfaPayload)
|
2022-03-14 19:54:41 +00:00
|
|
|
}
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
type FlagSetBit uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
FlagSetNone FlagSetBit = 1 << iota
|
|
|
|
FlagSetHTTP
|
|
|
|
FlagSetOutputField
|
|
|
|
FlagSetOutputFormat
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
FlagSetOutputDetailed
|
2017-08-28 20:44:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// flagSet creates the flags for this command. The result is cached on the
|
|
|
|
// command to save performance on future calls.
|
|
|
|
func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
|
|
|
|
c.flagsOnce.Do(func() {
|
|
|
|
set := NewFlagSets(c.UI)
|
|
|
|
|
2018-02-12 23:12:16 +00:00
|
|
|
// These flag sets will apply to all leaf subcommands.
|
|
|
|
// TODO: Optional, but FlagSetHTTP can be safely removed from the individual
|
|
|
|
// Flags() subcommands.
|
|
|
|
bit = bit | FlagSetHTTP
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
if bit&FlagSetHTTP != 0 {
|
|
|
|
f := set.NewFlagSet("HTTP Options")
|
|
|
|
|
2018-03-16 17:41:27 +00:00
|
|
|
addrStringVar := &StringVar{
|
2019-02-28 22:29:28 +00:00
|
|
|
Name: flagNameAddress,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagAddress,
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultAddress,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Address of the Vault server.",
|
2018-03-16 17:41:27 +00:00
|
|
|
}
|
|
|
|
if c.flagAddress != "" {
|
|
|
|
addrStringVar.Default = c.flagAddress
|
|
|
|
} else {
|
|
|
|
addrStringVar.Default = "https://127.0.0.1:8200"
|
|
|
|
}
|
|
|
|
f.StringVar(addrStringVar)
|
2017-08-28 20:44:35 +00:00
|
|
|
|
2019-02-15 01:10:36 +00:00
|
|
|
agentAddrStringVar := &StringVar{
|
|
|
|
Name: "agent-address",
|
2023-05-17 13:38:34 +00:00
|
|
|
Target: &c.flagAgentProxyAddress,
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultAgentAddr,
|
2019-02-15 01:10:36 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Address of the Agent.",
|
|
|
|
}
|
|
|
|
f.StringVar(agentAddrStringVar)
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
f.StringVar(&StringVar{
|
2019-02-28 22:29:28 +00:00
|
|
|
Name: flagNameCACert,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagCACert,
|
|
|
|
Default: "",
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultCACert,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictFiles("*"),
|
|
|
|
Usage: "Path on the local disk to a single PEM-encoded CA " +
|
|
|
|
"certificate to verify the Vault server's SSL certificate. This " +
|
2018-03-20 18:54:10 +00:00
|
|
|
"takes precedence over -ca-path.",
|
2017-08-28 20:44:35 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
f.StringVar(&StringVar{
|
2019-02-28 22:29:28 +00:00
|
|
|
Name: flagNameCAPath,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagCAPath,
|
|
|
|
Default: "",
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultCAPath,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictDirs("*"),
|
|
|
|
Usage: "Path on the local disk to a directory of PEM-encoded CA " +
|
|
|
|
"certificates to verify the Vault server's SSL certificate.",
|
|
|
|
})
|
|
|
|
|
|
|
|
f.StringVar(&StringVar{
|
2019-03-01 20:11:16 +00:00
|
|
|
Name: flagNameClientCert,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagClientCert,
|
|
|
|
Default: "",
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultClientCert,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictFiles("*"),
|
|
|
|
Usage: "Path on the local disk to a single PEM-encoded CA " +
|
|
|
|
"certificate to use for TLS authentication to the Vault server. If " +
|
|
|
|
"this flag is specified, -client-key is also required.",
|
|
|
|
})
|
|
|
|
|
|
|
|
f.StringVar(&StringVar{
|
2019-03-01 20:11:16 +00:00
|
|
|
Name: flagNameClientKey,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagClientKey,
|
|
|
|
Default: "",
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultClientKey,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictFiles("*"),
|
|
|
|
Usage: "Path on the local disk to a single PEM-encoded private key " +
|
|
|
|
"matching the client certificate from -client-cert.",
|
|
|
|
})
|
|
|
|
|
2018-08-10 16:13:06 +00:00
|
|
|
f.StringVar(&StringVar{
|
|
|
|
Name: "namespace",
|
|
|
|
Target: &c.flagNamespace,
|
2018-09-05 19:52:54 +00:00
|
|
|
Default: notSetValue, // this can never be a real value
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultNamespace,
|
2018-08-10 16:13:06 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "The namespace to use for the command. Setting this is not " +
|
2018-08-22 18:37:40 +00:00
|
|
|
"necessary but allows using relative paths. -ns can be used as " +
|
|
|
|
"shortcut.",
|
|
|
|
})
|
|
|
|
|
|
|
|
f.StringVar(&StringVar{
|
|
|
|
Name: "ns",
|
|
|
|
Target: &c.flagNS,
|
2018-09-05 19:52:54 +00:00
|
|
|
Default: notSetValue, // this can never be a real value
|
2018-08-22 18:37:40 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Hidden: true,
|
2018-08-27 16:03:39 +00:00
|
|
|
Usage: "Alias for -namespace. This takes precedence over -namespace.",
|
2018-08-10 16:13:06 +00:00
|
|
|
})
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
f.StringVar(&StringVar{
|
2019-10-29 13:11:01 +00:00
|
|
|
Name: flagTLSServerName,
|
2017-08-28 20:44:35 +00:00
|
|
|
Target: &c.flagTLSServerName,
|
|
|
|
Default: "",
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultTLSServerName,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Name to use as the SNI host when connecting to the Vault " +
|
|
|
|
"server via TLS.",
|
|
|
|
})
|
|
|
|
|
|
|
|
f.BoolVar(&BoolVar{
|
2019-02-28 22:29:28 +00:00
|
|
|
Name: flagNameTLSSkipVerify,
|
2017-09-05 03:54:13 +00:00
|
|
|
Target: &c.flagTLSSkipVerify,
|
|
|
|
Default: false,
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultSkipVerify,
|
2017-08-28 20:44:35 +00:00
|
|
|
Usage: "Disable verification of TLS certificates. Using this option " +
|
2019-03-05 20:19:52 +00:00
|
|
|
"is highly discouraged as it decreases the security of data " +
|
2017-08-28 20:44:35 +00:00
|
|
|
"transmissions to and from the Vault server.",
|
|
|
|
})
|
2018-11-20 17:33:00 +00:00
|
|
|
|
2022-09-30 08:29:37 +00:00
|
|
|
f.BoolVar(&BoolVar{
|
|
|
|
Name: flagNameDisableRedirects,
|
|
|
|
Target: &c.flagDisableRedirects,
|
|
|
|
Default: false,
|
|
|
|
EnvVar: api.EnvVaultDisableRedirects,
|
|
|
|
Usage: "Disable the default client behavior, which honors a single " +
|
|
|
|
"redirect response from a request",
|
|
|
|
})
|
|
|
|
|
2018-11-20 17:33:00 +00:00
|
|
|
f.BoolVar(&BoolVar{
|
|
|
|
Name: "policy-override",
|
|
|
|
Target: &c.flagPolicyOverride,
|
|
|
|
Default: false,
|
|
|
|
Usage: "Override a Sentinel policy that has a soft-mandatory " +
|
|
|
|
"enforcement_level specified",
|
|
|
|
})
|
2017-08-28 20:44:35 +00:00
|
|
|
|
|
|
|
f.DurationVar(&DurationVar{
|
|
|
|
Name: "wrap-ttl",
|
|
|
|
Target: &c.flagWrapTTL,
|
|
|
|
Default: 0,
|
2019-02-28 22:29:28 +00:00
|
|
|
EnvVar: api.EnvVaultWrapTTL,
|
2017-08-28 20:44:35 +00:00
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Wraps the response in a cubbyhole token with the requested " +
|
|
|
|
"TTL. The response is available via the \"vault unwrap\" command. " +
|
|
|
|
"The TTL is specified as a numeric string with suffix like \"30s\" " +
|
2017-09-05 03:54:13 +00:00
|
|
|
"or \"5m\".",
|
2017-08-28 20:44:35 +00:00
|
|
|
})
|
2018-03-30 16:11:10 +00:00
|
|
|
|
|
|
|
f.StringSliceVar(&StringSliceVar{
|
|
|
|
Name: "mfa",
|
|
|
|
Target: &c.flagMFA,
|
|
|
|
Default: nil,
|
|
|
|
EnvVar: api.EnvVaultMFA,
|
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Supply MFA credentials as part of X-Vault-MFA header.",
|
|
|
|
})
|
2019-02-01 22:13:51 +00:00
|
|
|
|
|
|
|
f.BoolVar(&BoolVar{
|
|
|
|
Name: "output-curl-string",
|
|
|
|
Target: &c.flagOutputCurlString,
|
|
|
|
Default: false,
|
|
|
|
Usage: "Instead of executing the request, print an equivalent cURL " +
|
|
|
|
"command string and exit.",
|
|
|
|
})
|
|
|
|
|
2022-04-27 23:35:18 +00:00
|
|
|
f.BoolVar(&BoolVar{
|
|
|
|
Name: "output-policy",
|
|
|
|
Target: &c.flagOutputPolicy,
|
|
|
|
Default: false,
|
|
|
|
Usage: "Instead of executing the request, print an example HCL " +
|
|
|
|
"policy that would be required to run this command, and exit.",
|
|
|
|
})
|
|
|
|
|
2021-10-22 20:22:49 +00:00
|
|
|
f.StringVar(&StringVar{
|
|
|
|
Name: "unlock-key",
|
|
|
|
Target: &c.flagUnlockKey,
|
|
|
|
Default: notSetValue,
|
|
|
|
Completion: complete.PredictNothing,
|
|
|
|
Usage: "Key to unlock a namespace API lock.",
|
|
|
|
})
|
|
|
|
|
2021-10-27 15:04:04 +00:00
|
|
|
f.StringMapVar(&StringMapVar{
|
|
|
|
Name: "header",
|
|
|
|
Target: &c.flagHeader,
|
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Key-value pair provided as key=value to provide http header added to any request done by the CLI." +
|
|
|
|
"Trying to add headers starting with 'X-Vault-' is forbidden and will make the command fail " +
|
|
|
|
"This can be specified multiple times.",
|
|
|
|
})
|
|
|
|
|
2022-02-24 20:16:15 +00:00
|
|
|
f.BoolVar(&BoolVar{
|
|
|
|
Name: "non-interactive",
|
|
|
|
Target: &c.flagNonInteractive,
|
|
|
|
Default: false,
|
|
|
|
Usage: "When set true, prevents asking the user for input via the terminal.",
|
|
|
|
})
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
if bit&(FlagSetOutputField|FlagSetOutputFormat|FlagSetOutputDetailed) != 0 {
|
|
|
|
outputSet := set.NewFlagSet("Output Options")
|
2017-08-28 20:44:35 +00:00
|
|
|
|
|
|
|
if bit&FlagSetOutputField != 0 {
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
outputSet.StringVar(&StringVar{
|
2017-08-28 20:44:35 +00:00
|
|
|
Name: "field",
|
|
|
|
Target: &c.flagField,
|
|
|
|
Default: "",
|
|
|
|
Completion: complete.PredictAnything,
|
|
|
|
Usage: "Print only the field with the given name. Specifying " +
|
|
|
|
"this option will take precedence over other formatting " +
|
|
|
|
"directives. The result will not have a trailing newline " +
|
2018-05-22 12:30:13 +00:00
|
|
|
"making it ideal for piping to other processes.",
|
2017-08-28 20:44:35 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if bit&FlagSetOutputFormat != 0 {
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
outputSet.StringVar(&StringVar{
|
2017-08-28 20:44:35 +00:00
|
|
|
Name: "format",
|
|
|
|
Target: &c.flagFormat,
|
|
|
|
Default: "table",
|
2018-02-12 23:12:16 +00:00
|
|
|
EnvVar: EnvVaultFormat,
|
2022-10-28 16:53:23 +00:00
|
|
|
Completion: complete.PredictSet("table", "json", "yaml", "pretty", "raw"),
|
2021-03-03 18:59:50 +00:00
|
|
|
Usage: `Print the output in the given format. Valid formats
|
2022-10-28 16:53:23 +00:00
|
|
|
are "table", "json", "yaml", or "pretty". "raw" is allowed
|
|
|
|
for 'vault read' operations only.`,
|
2017-08-28 20:44:35 +00:00
|
|
|
})
|
|
|
|
}
|
Vault CLI: show detailed information with ListResponseWithInfo (#15417)
* CLI: Add ability to display ListResponseWithInfos
The Vault Server API includes a ListResponseWithInfo call, allowing LIST
responses to contain additional information about their keys. This is in
a key=value mapping format (both for each key, to get the additional
metadata, as well as within each metadata).
Expand the `vault list` CLI command with a `-detailed` flag (and env var
VAULT_DETAILED_LISTS) to print this additional metadata. This looks
roughly like the following:
$ vault list -detailed pki/issuers
Keys issuer_name
---- -----------
0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7 n/a
35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0 n/a
382fad1e-e99c-9c54-e147-bb1faa8033d3 n/a
8bb4a793-2ad9-460c-9fa8-574c84a981f7 n/a
8bd231d7-20e2-f21f-ae1a-7aa3319715e7 n/a
9425d51f-cb81-426d-d6ad-5147d092094e n/a
ae679732-b497-ab0d-3220-806a2b9d81ed n/a
c5a44a1f-2ae4-2140-3acf-74b2609448cc utf8
d41d2419-efce-0e36-c96b-e91179a24dc1 something
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Allow detailed printing of LIST responses in JSON
When using the JSON formatter, only the absolute list of keys were
returned. Reuse the `-detailed` flag value for the `-format=json` list
response printer, allowing us to show the complete API response returned
by Vault.
This returns something like the following:
{
"request_id": "e9a25dcd-b67a-97d7-0f08-3670918ef3ff",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"key_info": {
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7": {
"issuer_name": ""
},
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0": {
"issuer_name": ""
},
"382fad1e-e99c-9c54-e147-bb1faa8033d3": {
"issuer_name": ""
},
"8bb4a793-2ad9-460c-9fa8-574c84a981f7": {
"issuer_name": ""
},
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7": {
"issuer_name": ""
},
"9425d51f-cb81-426d-d6ad-5147d092094e": {
"issuer_name": ""
},
"ae679732-b497-ab0d-3220-806a2b9d81ed": {
"issuer_name": ""
},
"c5a44a1f-2ae4-2140-3acf-74b2609448cc": {
"issuer_name": "utf8"
},
"d41d2419-efce-0e36-c96b-e91179a24dc1": {
"issuer_name": "something"
}
},
"keys": [
"0cba84d7-bbbe-836a-4ff6-a11b31dc0fb7",
"35dfb02d-0cdb-3d35-ee64-d0cd6568c6b0",
"382fad1e-e99c-9c54-e147-bb1faa8033d3",
"8bb4a793-2ad9-460c-9fa8-574c84a981f7",
"8bd231d7-20e2-f21f-ae1a-7aa3319715e7",
"9425d51f-cb81-426d-d6ad-5147d092094e",
"ae679732-b497-ab0d-3220-806a2b9d81ed",
"c5a44a1f-2ae4-2140-3acf-74b2609448cc",
"d41d2419-efce-0e36-c96b-e91179a24dc1"
]
},
"warnings": null
}
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use field on UI rather than secret.Data
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Only include headers from visitable key_infos
Certain API endpoints return data from non-visitable key_infos, by
virtue of using a hand-rolled response. Limit our headers to those
from visitable key_infos. This means we won't return entire columns with
n/a entries, if no key matches the key_info key that includes that
header.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Use setupEnv sourced detailed info
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix changelog environment variable
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Fix broken tests using setupEnv
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-05-18 17:00:50 +00:00
|
|
|
|
|
|
|
if bit&FlagSetOutputDetailed != 0 {
|
|
|
|
outputSet.BoolVar(&BoolVar{
|
|
|
|
Name: "detailed",
|
|
|
|
Target: &c.flagDetailed,
|
|
|
|
Default: false,
|
|
|
|
EnvVar: EnvVaultDetailed,
|
|
|
|
Usage: "Enables additional metadata during some operations",
|
|
|
|
})
|
|
|
|
}
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c.flags = set
|
|
|
|
})
|
|
|
|
|
|
|
|
return c.flags
|
|
|
|
}
|
|
|
|
|
|
|
|
// FlagSets is a group of flag sets.
|
|
|
|
type FlagSets struct {
|
|
|
|
flagSets []*FlagSet
|
|
|
|
mainSet *flag.FlagSet
|
|
|
|
hiddens map[string]struct{}
|
|
|
|
completions complete.Flags
|
2022-07-27 18:00:03 +00:00
|
|
|
ui cli.Ui
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFlagSets creates a new flag sets.
|
|
|
|
func NewFlagSets(ui cli.Ui) *FlagSets {
|
|
|
|
mainSet := flag.NewFlagSet("", flag.ContinueOnError)
|
|
|
|
|
2017-08-30 16:48:39 +00:00
|
|
|
// Errors and usage are controlled by the CLI.
|
|
|
|
mainSet.Usage = func() {}
|
|
|
|
mainSet.SetOutput(ioutil.Discard)
|
2017-08-28 20:44:35 +00:00
|
|
|
|
|
|
|
return &FlagSets{
|
|
|
|
flagSets: make([]*FlagSet, 0, 6),
|
|
|
|
mainSet: mainSet,
|
|
|
|
hiddens: make(map[string]struct{}),
|
|
|
|
completions: complete.Flags{},
|
2022-07-27 18:00:03 +00:00
|
|
|
ui: ui,
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFlagSet creates a new flag set from the given flag sets.
|
|
|
|
func (f *FlagSets) NewFlagSet(name string) *FlagSet {
|
|
|
|
flagSet := NewFlagSet(name)
|
2017-09-05 03:53:13 +00:00
|
|
|
flagSet.mainSet = f.mainSet
|
|
|
|
flagSet.completions = f.completions
|
|
|
|
f.flagSets = append(f.flagSets, flagSet)
|
2017-08-28 20:44:35 +00:00
|
|
|
return flagSet
|
|
|
|
}
|
|
|
|
|
2017-09-05 03:53:13 +00:00
|
|
|
// Completions returns the completions for this flag set.
|
2017-08-28 20:44:35 +00:00
|
|
|
func (f *FlagSets) Completions() complete.Flags {
|
|
|
|
return f.completions
|
|
|
|
}
|
|
|
|
|
2022-10-28 16:53:23 +00:00
|
|
|
type (
|
|
|
|
ParseOptions interface{}
|
|
|
|
ParseOptionAllowRawFormat bool
|
2023-05-08 16:15:44 +00:00
|
|
|
DisableDisplayFlagWarning bool
|
2022-10-28 16:53:23 +00:00
|
|
|
)
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
// Parse parses the given flags, returning any errors.
|
2022-07-27 18:00:03 +00:00
|
|
|
// Warnings, if any, regarding the arguments format are sent to stdout
|
2022-10-28 16:53:23 +00:00
|
|
|
func (f *FlagSets) Parse(args []string, opts ...ParseOptions) error {
|
2022-07-27 18:00:03 +00:00
|
|
|
err := f.mainSet.Parse(args)
|
|
|
|
|
2023-05-08 16:15:44 +00:00
|
|
|
displayFlagWarningsDisabled := false
|
|
|
|
for _, opt := range opts {
|
|
|
|
if value, ok := opt.(DisableDisplayFlagWarning); ok {
|
|
|
|
displayFlagWarningsDisabled = bool(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !displayFlagWarningsDisabled {
|
|
|
|
warnings := generateFlagWarnings(f.Args())
|
|
|
|
if warnings != "" && Format(f.ui) == "table" {
|
|
|
|
f.ui.Warn(warnings)
|
|
|
|
}
|
2022-07-27 18:00:03 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 16:53:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now surface any other errors.
|
|
|
|
return generateFlagErrors(f, opts...)
|
2017-08-28 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 23:12:16 +00:00
|
|
|
// Parsed reports whether the command-line flags have been parsed.
|
|
|
|
func (f *FlagSets) Parsed() bool {
|
|
|
|
return f.mainSet.Parsed()
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
// Args returns the remaining args after parsing.
|
|
|
|
func (f *FlagSets) Args() []string {
|
|
|
|
return f.mainSet.Args()
|
|
|
|
}
|
|
|
|
|
2018-03-09 19:32:28 +00:00
|
|
|
// Visit visits the flags in lexicographical order, calling fn for each. It
|
|
|
|
// visits only those flags that have been set.
|
|
|
|
func (f *FlagSets) Visit(fn func(*flag.Flag)) {
|
|
|
|
f.mainSet.Visit(fn)
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:44:35 +00:00
|
|
|
// Help builds custom help for this command, grouping by flag set.
|
2022-07-27 18:00:03 +00:00
|
|
|
func (f *FlagSets) Help() string {
|
2017-08-28 20:44:35 +00:00
|
|
|
var out bytes.Buffer
|
|
|
|
|
2022-07-27 18:00:03 +00:00
|
|
|
for _, set := range f.flagSets {
|
2017-08-28 20:44:35 +00:00
|
|
|
printFlagTitle(&out, set.name+":")
|
|
|
|
set.VisitAll(func(f *flag.Flag) {
|
|
|
|
// Skip any hidden flags
|
2017-09-05 03:53:13 +00:00
|
|
|
if v, ok := f.Value.(FlagVisibility); ok && v.Hidden() {
|
2017-08-28 20:44:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
printFlagDetail(&out, f)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.TrimRight(out.String(), "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
// FlagSet is a grouped wrapper around a real flag set and a grouped flag set.
|
|
|
|
type FlagSet struct {
|
|
|
|
name string
|
|
|
|
flagSet *flag.FlagSet
|
|
|
|
mainSet *flag.FlagSet
|
|
|
|
completions complete.Flags
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFlagSet creates a new flag set.
|
|
|
|
func NewFlagSet(name string) *FlagSet {
|
|
|
|
return &FlagSet{
|
|
|
|
name: name,
|
|
|
|
flagSet: flag.NewFlagSet(name, flag.ContinueOnError),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the name of this flag set.
|
|
|
|
func (f *FlagSet) Name() string {
|
|
|
|
return f.name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FlagSet) Visit(fn func(*flag.Flag)) {
|
|
|
|
f.flagSet.Visit(fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FlagSet) VisitAll(fn func(*flag.Flag)) {
|
|
|
|
f.flagSet.VisitAll(fn)
|
|
|
|
}
|
2017-09-05 03:53:13 +00:00
|
|
|
|
|
|
|
// printFlagTitle prints a consistently-formatted title to the given writer.
|
|
|
|
func printFlagTitle(w io.Writer, s string) {
|
|
|
|
fmt.Fprintf(w, "%s\n\n", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
// printFlagDetail prints a single flag to the given writer.
|
|
|
|
func printFlagDetail(w io.Writer, f *flag.Flag) {
|
|
|
|
// Check if the flag is hidden - do not print any flag detail or help output
|
|
|
|
// if it is hidden.
|
|
|
|
if h, ok := f.Value.(FlagVisibility); ok && h.Hidden() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a detailed example
|
|
|
|
example := ""
|
|
|
|
if t, ok := f.Value.(FlagExample); ok {
|
|
|
|
example = t.Example()
|
|
|
|
}
|
|
|
|
|
|
|
|
if example != "" {
|
|
|
|
fmt.Fprintf(w, " -%s=<%s>\n", f.Name, example)
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, " -%s\n", f.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
usage := reRemoveWhitespace.ReplaceAllString(f.Usage, " ")
|
|
|
|
indented := wrapAtLengthWithPadding(usage, 6)
|
|
|
|
fmt.Fprintf(w, "%s\n\n", indented)
|
|
|
|
}
|