Update audit-list command

This commit is contained in:
Seth Vargo 2017-09-04 23:59:13 -04:00
parent 8e343caeda
commit ac0be24253
No known key found for this signature in database
GPG Key ID: C921994F9C27E0FF
2 changed files with 234 additions and 94 deletions

View File

@ -5,83 +5,162 @@ import (
"sort"
"strings"
"github.com/hashicorp/vault/meta"
"github.com/ryanuber/columnize"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
// Ensure we are implementing the right interfaces.
var _ cli.Command = (*AuditListCommand)(nil)
var _ cli.CommandAutocomplete = (*AuditListCommand)(nil)
// AuditListCommand is a Command that lists the enabled audits.
type AuditListCommand struct {
meta.Meta
}
*BaseCommand
func (c *AuditListCommand) Run(args []string) int {
flags := c.Meta.FlagSet("audit-list", meta.FlagSetDefault)
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client: %s", err))
return 2
}
audits, err := client.Sys().ListAudit()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error reading audits: %s", err))
return 2
}
if len(audits) == 0 {
c.Ui.Error(fmt.Sprintf(
"No audit backends are enabled. Use `vault audit-enable` to\n" +
"enable an audit backend."))
return 1
}
paths := make([]string, 0, len(audits))
for path, _ := range audits {
paths = append(paths, path)
}
sort.Strings(paths)
columns := []string{"Path | Type | Description | Replication Behavior | Options"}
for _, path := range paths {
audit := audits[path]
opts := make([]string, 0, len(audit.Options))
for k, v := range audit.Options {
opts = append(opts, k+"="+v)
}
replicatedBehavior := "replicated"
if audit.Local {
replicatedBehavior = "local"
}
columns = append(columns, fmt.Sprintf(
"%s | %s | %s | %s | %s", audit.Path, audit.Type, audit.Description, replicatedBehavior, strings.Join(opts, " ")))
}
c.Ui.Output(columnize.SimpleFormat(columns))
return 0
flagDetailed bool
}
func (c *AuditListCommand) Synopsis() string {
return "Lists enabled audit backends in Vault"
return "Lists enabled audit backends"
}
func (c *AuditListCommand) Help() string {
helpText := `
Usage: vault audit-list [options]
List the enabled audit backends.
Lists the enabled audit backends in the Vault server. The output lists
the enabled audit backends and the options for those backends.
The output lists the enabled audit backends and the options for those
backends. The options may contain sensitive information, and therefore
only a root Vault user can view this.
List all audit backends:
$ vault audit-list
List detailed output about the audit backends:
$ vault audit-list -detailed
For a full list of examples, please see the documentation.
` + c.Flags().Help()
General Options:
` + meta.GeneralOptionsUsage()
return strings.TrimSpace(helpText)
}
func (c *AuditListCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP)
f := set.NewFlagSet("Command Options")
f.BoolVar(&BoolVar{
Name: "detailed",
Target: &c.flagDetailed,
Default: false,
EnvVar: "",
Usage: "Print detailed information such as options and replication " +
"status about each mount.",
})
return set
}
func (c *AuditListCommand) AutocompleteArgs() complete.Predictor {
return nil
}
func (c *AuditListCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}
func (c *AuditListCommand) Run(args []string) int {
f := c.Flags()
if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}
args = f.Args()
if len(args) > 0 {
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", len(args)))
return 1
}
client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
return 2
}
audits, err := client.Sys().ListAudit()
if err != nil {
c.UI.Error(fmt.Sprintf("Error listing audits: %s", err))
return 2
}
if len(audits) == 0 {
c.UI.Error(fmt.Sprintf("No audit backends are enabled."))
return 0
}
if c.flagDetailed {
c.UI.Output(tableOutput(c.detailedAudits(audits)))
return 0
}
c.UI.Output(tableOutput(c.simpleAudits(audits)))
return 0
}
func (c *AuditListCommand) simpleAudits(audits map[string]*api.Audit) []string {
paths := make([]string, 0, len(audits))
for path, _ := range audits {
paths = append(paths, path)
}
sort.Strings(paths)
columns := []string{"Path | Type | Description"}
for _, path := range paths {
audit := audits[path]
columns = append(columns, fmt.Sprintf("%s | %s | %s",
audit.Path,
audit.Type,
audit.Description,
))
}
return columns
}
func (c *AuditListCommand) detailedAudits(audits map[string]*api.Audit) []string {
paths := make([]string, 0, len(audits))
for path, _ := range audits {
paths = append(paths, path)
}
sort.Strings(paths)
columns := []string{"Path | Type | Description | Replication | Options"}
for _, path := range paths {
audit := audits[path]
opts := make([]string, 0, len(audit.Options))
for k, v := range audit.Options {
opts = append(opts, k+"="+v)
}
replication := "replicated"
if audit.Local {
replication = "local"
}
columns = append(columns, fmt.Sprintf("%s | %s | %s | %s | %s",
audit.Path,
audit.Type,
audit.Description,
replication,
strings.Join(opts, " "),
))
}
return columns
}

View File

@ -1,50 +1,111 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/meta"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/cli"
)
func TestAuditList(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := http.TestServer(t, core)
defer ln.Close()
func testAuditListCommand(tb testing.TB) (*cli.MockUi, *AuditListCommand) {
tb.Helper()
ui := new(cli.MockUi)
c := &AuditListCommand{
Meta: meta.Meta{
ClientToken: token,
Ui: ui,
ui := cli.NewMockUi()
return ui, &AuditListCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
}
func TestAuditListCommand_Run(t *testing.T) {
t.Parallel()
cases := []struct {
name string
args []string
out string
code int
}{
{
"too_many_args",
[]string{"foo"},
"Too many arguments",
1,
},
{
"lists",
nil,
"Path",
0,
},
{
"detailed",
[]string{"-detailed"},
"Options",
0,
},
}
args := []string{
"-address", addr,
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()
if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{
Type: "file",
Options: map[string]string{
"file_path": "discard",
},
}); err != nil {
t.Fatal(err)
}
ui, cmd := testAuditListCommand(t)
cmd.client = client
code := cmd.Run(tc.args)
if code != tc.code {
t.Errorf("expected %d to be %d", code, tc.code)
}
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, tc.out) {
t.Errorf("expected %q to contain %q", combined, tc.out)
}
})
}
// Run once to get the client
c.Run(args)
t.Run("communication_failure", func(t *testing.T) {
t.Parallel()
// Get the client
client, err := c.Client()
if err != nil {
t.Fatalf("err: %#v", err)
}
if err := client.Sys().EnableAuditWithOptions("foo", &api.EnableAuditOptions{
Type: "noop",
Description: "noop",
Options: nil,
}); err != nil {
t.Fatalf("err: %#v", err)
}
client, closer := testVaultServerBad(t)
defer closer()
// Run again
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
ui, cmd := testAuditListCommand(t)
cmd.client = client
code := cmd.Run([]string{})
if exp := 2; code != exp {
t.Errorf("expected %d to be %d", code, exp)
}
expected := "Error listing audits: "
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, expected) {
t.Errorf("expected %q to contain %q", combined, expected)
}
})
t.Run("no_tabs", func(t *testing.T) {
t.Parallel()
_, cmd := testAuditListCommand(t)
assertNoTabs(t, cmd)
})
}