mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 02:02:43 +00:00
Command: token capabilities using accessor (#24479)
* Command: token capabilities using accessor * release note * Apply suggestions from code review Co-authored-by: Marc Boudreau <marc.boudreau@hashicorp.com> --------- Co-authored-by: Marc Boudreau <marc.boudreau@hashicorp.com>
This commit is contained in:
committed by
GitHub
parent
dc5c3e8d97
commit
e4ffe8979c
@@ -78,3 +78,56 @@ func (c *Sys) CapabilitiesWithContext(ctx context.Context, token, path string) (
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Sys) CapabilitiesAccessor(accessor, path string) ([]string, error) {
|
||||
return c.CapabilitiesAccessorWithContext(context.Background(), accessor, path)
|
||||
}
|
||||
|
||||
func (c *Sys) CapabilitiesAccessorWithContext(ctx context.Context, accessor, path string) ([]string, error) {
|
||||
ctx, cancelFunc := c.c.withConfiguredTimeout(ctx)
|
||||
defer cancelFunc()
|
||||
|
||||
body := map[string]string{
|
||||
"accessor": accessor,
|
||||
"path": path,
|
||||
}
|
||||
|
||||
reqPath := "/v1/sys/capabilities-accessor"
|
||||
|
||||
r := c.c.NewRequest(http.MethodPost, reqPath)
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.rawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
secret, err := ParseSecret(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
return nil, errors.New("data from server response is empty")
|
||||
}
|
||||
|
||||
var res []string
|
||||
err = mapstructure.Decode(secret.Data[path], &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
_, ok := secret.Data["capabilities"]
|
||||
if ok {
|
||||
err = mapstructure.Decode(secret.Data["capabilities"], &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
3
changelog/24479.txt
Normal file
3
changelog/24479.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:improvement
|
||||
command/token-capabilities: allow using accessor when listing token capabilities on a path
|
||||
```
|
||||
@@ -19,6 +19,8 @@ var (
|
||||
|
||||
type TokenCapabilitiesCommand struct {
|
||||
*BaseCommand
|
||||
|
||||
flagAccessor bool
|
||||
}
|
||||
|
||||
func (c *TokenCapabilitiesCommand) Synopsis() string {
|
||||
@@ -27,12 +29,15 @@ func (c *TokenCapabilitiesCommand) Synopsis() string {
|
||||
|
||||
func (c *TokenCapabilitiesCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: vault token capabilities [options] [TOKEN] PATH
|
||||
Usage: vault token capabilities [options] [TOKEN | ACCESSOR] PATH
|
||||
|
||||
Fetches the capabilities of a token for a given path. If a TOKEN is provided
|
||||
as an argument, the "/sys/capabilities" endpoint and permission is used. If
|
||||
no TOKEN is provided, the "/sys/capabilities-self" endpoint and permission
|
||||
is used with the locally authenticated token.
|
||||
Fetches the capabilities of a token or accessor for a given path. If a TOKEN
|
||||
is provided as an argument, the "/sys/capabilities" endpoint is used, which
|
||||
returns the capabilities of the provided TOKEN. If an ACCESSOR is provided
|
||||
as an argument along with the -accessor option, the "/sys/capabilities-accessor"
|
||||
endpoint is used, which returns the capabilities of the token referenced by
|
||||
ACCESSOR. If no TOKEN is provided, the "/sys/capabilities-self" endpoint
|
||||
is used, which returns the capabilities of the locally authenticated token.
|
||||
|
||||
List capabilities for the local token on the "secret/foo" path:
|
||||
|
||||
@@ -42,6 +47,10 @@ Usage: vault token capabilities [options] [TOKEN] PATH
|
||||
|
||||
$ vault token capabilities 96ddf4bc-d217-f3ba-f9bd-017055595017 cubbyhole/foo
|
||||
|
||||
List capabilities for a token on the "cubbyhole/foo" path via its accessor:
|
||||
|
||||
$ vault token capabilities -accessor 9793c9b3-e04a-46f3-e7b8-748d7da248da cubbyhole/foo
|
||||
|
||||
For a full list of examples, please see the documentation.
|
||||
|
||||
` + c.Flags().Help()
|
||||
@@ -50,7 +59,20 @@ Usage: vault token capabilities [options] [TOKEN] PATH
|
||||
}
|
||||
|
||||
func (c *TokenCapabilitiesCommand) Flags() *FlagSets {
|
||||
return c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.BoolVar(&BoolVar{
|
||||
Name: "accessor",
|
||||
Target: &c.flagAccessor,
|
||||
Default: false,
|
||||
EnvVar: "",
|
||||
Completion: complete.PredictNothing,
|
||||
Usage: "Treat the argument as an accessor instead of a token.",
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (c *TokenCapabilitiesCommand) AutocompleteArgs() complete.Predictor {
|
||||
@@ -72,13 +94,19 @@ func (c *TokenCapabilitiesCommand) Run(args []string) int {
|
||||
token := ""
|
||||
path := ""
|
||||
args = f.Args()
|
||||
switch len(args) {
|
||||
case 0:
|
||||
switch {
|
||||
case c.flagAccessor && len(args) < 2:
|
||||
c.UI.Error(fmt.Sprintf("Not enough arguments with -accessor (expected 2, got %d)", len(args)))
|
||||
return 1
|
||||
case c.flagAccessor && len(args) > 2:
|
||||
c.UI.Error(fmt.Sprintf("Too many arguments with -accessor (expected 2, got %d)", len(args)))
|
||||
return 1
|
||||
case len(args) == 0:
|
||||
c.UI.Error("Not enough arguments (expected 1-2, got 0)")
|
||||
return 1
|
||||
case 1:
|
||||
case len(args) == 1:
|
||||
path = args[0]
|
||||
case 2:
|
||||
case len(args) == 2:
|
||||
token, path = args[0], args[1]
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Too many arguments (expected 1-2, got %d)", len(args)))
|
||||
@@ -92,11 +120,15 @@ func (c *TokenCapabilitiesCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
var capabilities []string
|
||||
if token == "" {
|
||||
switch {
|
||||
case token == "":
|
||||
capabilities, err = client.Sys().CapabilitiesSelf(path)
|
||||
} else {
|
||||
case c.flagAccessor:
|
||||
capabilities, err = client.Sys().CapabilitiesAccessor(token, path)
|
||||
default:
|
||||
capabilities, err = client.Sys().Capabilities(token, path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error listing capabilities: %s", err))
|
||||
return 2
|
||||
|
||||
@@ -31,6 +31,24 @@ func TestTokenCapabilitiesCommand_Run(t *testing.T) {
|
||||
out string
|
||||
code int
|
||||
}{
|
||||
{
|
||||
"accessor_no_args",
|
||||
[]string{"-accessor"},
|
||||
"Not enough arguments",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"accessor_too_few_args",
|
||||
[]string{"-accessor", "abcd1234"},
|
||||
"Not enough arguments",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"accessor_too_many_args",
|
||||
[]string{"-accessor", "abcd1234", "efgh5678", "ijkl9012"},
|
||||
"Too many arguments",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"too_many_args",
|
||||
[]string{"foo", "bar", "zip"},
|
||||
@@ -103,6 +121,48 @@ func TestTokenCapabilitiesCommand_Run(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("accessor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
policy := `path "secret/foo" { capabilities = ["read"] }`
|
||||
if err := client.Sys().PutPolicy("policy", policy); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
|
||||
Policies: []string{"policy"},
|
||||
TTL: "30m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if secret == nil || secret.Auth == nil || secret.Auth.ClientToken == "" {
|
||||
t.Fatalf("missing auth data: %#v", secret)
|
||||
}
|
||||
accessor := secret.Auth.Accessor
|
||||
|
||||
ui, cmd := testTokenCapabilitiesCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"-accessor",
|
||||
accessor,
|
||||
"secret/foo",
|
||||
})
|
||||
if exp := 0; code != exp {
|
||||
t.Errorf("expected %d to be %d", code, exp)
|
||||
}
|
||||
|
||||
expected := "read"
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
if !strings.Contains(combined, expected) {
|
||||
t.Errorf("expected %q to contain %q", combined, expected)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("local", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user