Skip to content

Commit

Permalink
CLI: add new -header option to be able to add headers to all cli requ…
Browse files Browse the repository at this point in the history
…ests #8754
  • Loading branch information
ylorenzati committed Oct 27, 2021
1 parent c97e44e commit 10e21d3
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
3 changes: 3 additions & 0 deletions changelog/12508.txt
@@ -0,0 +1,3 @@
```release-note:improvement
cli: add new http option : -header which enable sending arbitrary headers with the cli
```
28 changes: 28 additions & 0 deletions command/base.go
Expand Up @@ -58,6 +58,8 @@ type BaseCommand struct {

flagMFA []string

flagHeader map[string]string

tokenHelper token.TokenHelper

client *api.Client
Expand Down Expand Up @@ -154,6 +156,23 @@ func (c *BaseCommand) Client() (*api.Client, error) {
client.SetPolicyOverride(c.flagPolicyOverride)
}

if c.flagHeader != nil {

var forbiddenHeaders []string
for key, val := range c.flagHeader {
const prefix = "X-Vault-"
if strings.HasPrefix(key, prefix) {
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, ", "))
}
}

c.client = client

return client, nil
Expand Down Expand Up @@ -365,6 +384,15 @@ func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
Usage: "Key to unlock a namespace API lock.",
})

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.",
})

}

if bit&(FlagSetOutputField|FlagSetOutputFormat) != 0 {
Expand Down
69 changes: 69 additions & 0 deletions command/base_test.go
@@ -0,0 +1,69 @@
package command

import (
"net/http"
"reflect"
"testing"
)

func getDefaultCliHeaders(t *testing.T) http.Header {
bc := &BaseCommand{}
cli, err := bc.Client()
if err != nil {
t.Fatal(err)
}
return cli.Headers()
}

func TestClient_FlagHeader(t *testing.T) {
defaultHeaders := getDefaultCliHeaders(t)

cases := []struct {
Input map[string]string
Valid bool
}{
{
map[string]string{},
true,
},
{
map[string]string{"foo": "bar", "header2": "value2"},
true,
},
{
map[string]string{"X-Vault-foo": "bar", "header2": "value2"},
false,
},
}

for _, tc := range cases {
expectedHeaders := defaultHeaders.Clone()
for key, val := range tc.Input {
expectedHeaders.Add(key, val)
}

bc := &BaseCommand{flagHeader: tc.Input}
cli, err := bc.Client()

if err == nil && !tc.Valid {
t.Errorf("No error for input[%#v], but not valid", tc.Input)
continue
}

if err != nil {
if tc.Valid {
t.Errorf("Error[%v] with input[%#v], but valid", err, tc.Input)
}
continue
}

if cli == nil {
t.Error("client should not be nil")
}

actualHeaders := cli.Headers()
if !reflect.DeepEqual(expectedHeaders, actualHeaders) {
t.Errorf("expected [%#v] but got [%#v]", expectedHeaders, actualHeaders)
}
}
}

0 comments on commit 10e21d3

Please sign in to comment.