diff --git a/changelog/10490.txt b/changelog/10490.txt new file mode 100644 index 000000000..81aadcd07 --- /dev/null +++ b/changelog/10490.txt @@ -0,0 +1,3 @@ +```release-note:bug +api/sys/config/ui: Fixes issue where multiple UI custom header values are ignored and only the first given value is used +``` diff --git a/vault/logical_system.go b/vault/logical_system.go index b5e3775b8..29076a880 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -2432,18 +2432,28 @@ func (b *SystemBackend) handleDisableAudit(ctx context.Context, req *logical.Req func (b *SystemBackend) handleConfigUIHeadersRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { header := data.Get("header").(string) + multivalue := data.Get("multivalue").(bool) - value, err := b.Core.uiConfig.GetHeader(ctx, header) + values, err := b.Core.uiConfig.GetHeader(ctx, header) if err != nil { return nil, err } - if value == "" { + if len(values) == 0 { return nil, nil } + // Return multiple values if specified + if multivalue { + return &logical.Response{ + Data: map[string]interface{}{ + "values": values, + }, + }, nil + } + return &logical.Response{ Data: map[string]interface{}{ - "value": value, + "value": values[0], }, }, nil } @@ -2477,7 +2487,7 @@ func (b *SystemBackend) handleConfigUIHeadersUpdate(ctx context.Context, req *lo for _, v := range values { value.Add(header, v) } - err := b.Core.uiConfig.SetHeader(ctx, header, value.Get(header)) + err := b.Core.uiConfig.SetHeader(ctx, header, value.Values(header)) if err != nil { return nil, err } diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go index 10787ebad..b47ab548b 100644 --- a/vault/logical_system_paths.go +++ b/vault/logical_system_paths.go @@ -71,6 +71,10 @@ func (b *SystemBackend) configPaths() []*framework.Path { Type: framework.TypeStringSlice, Description: "The values to set the header.", }, + "multivalue": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "Returns multiple values if true", + }, }, Operations: map[logical.Operation]framework.OperationHandler{ diff --git a/vault/ui.go b/vault/ui.go index 5b6f7c496..952137da3 100644 --- a/vault/ui.go +++ b/vault/ui.go @@ -92,25 +92,25 @@ func (c *UIConfig) HeaderKeys(ctx context.Context) ([]string, error) { return keys, nil } -// GetHeader retrieves the configured value for the given header -func (c *UIConfig) GetHeader(ctx context.Context, header string) (string, error) { +// GetHeader retrieves the configured values for the given header +func (c *UIConfig) GetHeader(ctx context.Context, header string) ([]string, error) { c.l.RLock() defer c.l.RUnlock() config, err := c.get(ctx) if err != nil { - return "", err + return nil, err } if config == nil { - return "", nil + return nil, nil } - value := config.Headers.Get(header) + value := config.Headers.Values(header) return value, nil } -// SetHeader sets the value for the given header -func (c *UIConfig) SetHeader(ctx context.Context, header, value string) error { +// SetHeader sets the values for the given header +func (c *UIConfig) SetHeader(ctx context.Context, header string, values []string) error { c.l.Lock() defer c.l.Unlock() @@ -123,7 +123,14 @@ func (c *UIConfig) SetHeader(ctx context.Context, header, value string) error { Headers: http.Header{}, } } - config.Headers.Set(header, value) + + // Clear custom header values before setting new + config.Headers.Del(header) + + // Set new values + for _, value := range values { + config.Headers.Add(header, value) + } return c.save(ctx, config) } diff --git a/vault/ui_test.go b/vault/ui_test.go index 702dde445..f2b2d148f 100644 --- a/vault/ui_test.go +++ b/vault/ui_test.go @@ -51,10 +51,10 @@ func TestConfig_Headers(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if head != "" { + if len(head) != 0 { t.Fatal("header returned found, should not be found") } - err = config.SetHeader(context.Background(), "Test-Header", "123") + err = config.SetHeader(context.Background(), "Test-Header", []string{"123", "456"}) if err != nil { t.Fatalf("err: %v", err) } @@ -62,22 +62,28 @@ func TestConfig_Headers(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if head == "" { - t.Fatal("header not found when it should be") + if len(head) != 2 { + t.Fatalf("header not found or incorrect number of values: %#v", head) } - if head != "123" { - t.Fatalf("expected: %s, got: %s", "123", head) + if head[0] != "123" { + t.Fatalf("expected: %s, got: %s", "123", head[0]) + } + if head[1] != "456" { + t.Fatalf("expected: %s, got: %s", "456", head[1]) } head, err = config.GetHeader(context.Background(), "tEST-hEADER") if err != nil { t.Fatalf("err: %v", err) } - if head == "" { - t.Fatal("header not found when it should be") + if len(head) != 2 { + t.Fatalf("header not found or incorrect number of values: %#v", head) } - if head != "123" { - t.Fatalf("expected: %s, got: %s", "123", head) + if head[0] != "123" { + t.Fatalf("expected: %s, got: %s", "123", head[0]) + } + if head[1] != "456" { + t.Fatalf("expected: %s, got: %s", "456", head[1]) } keys, err := config.HeaderKeys(context.Background()) @@ -88,7 +94,7 @@ func TestConfig_Headers(t *testing.T) { t.Fatalf("expected 1 key, got %d", len(keys)) } - err = config.SetHeader(context.Background(), "Test-Header-2", "321") + err = config.SetHeader(context.Background(), "Test-Header-2", []string{"321"}) if err != nil { t.Fatalf("err: %v", err) } @@ -112,7 +118,7 @@ func TestConfig_Headers(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if head != "" { + if len(head) != 0 { t.Fatal("header returned found, should not be found") } keys, err = config.HeaderKeys(context.Background()) @@ -151,7 +157,7 @@ func TestConfig_DefaultHeaders(t *testing.T) { t.Fatalf("header does not match: expected %s, got %s", defaultCSP, head) } - err = config.SetHeader(context.Background(), "Content-security-Policy", "test") + err = config.SetHeader(context.Background(), "Content-security-Policy", []string{"test"}) if err != nil { t.Fatalf("err: %v", err) } diff --git a/website/pages/api-docs/system/config-ui.mdx b/website/pages/api-docs/system/config-ui.mdx index 7fc935637..0f150e060 100644 --- a/website/pages/api-docs/system/config-ui.mdx +++ b/website/pages/api-docs/system/config-ui.mdx @@ -24,6 +24,8 @@ This endpoint returns the given UI header configuration. - `name` `(string: )` – The name of the custom header. +- `multivalue` `(bool: )` - Returns multiple values if true. + ### Sample Request ```shell-session @@ -40,6 +42,22 @@ $ curl \ } ``` +### Sample Request (Multi value) + +```shell-session +$ curl \ + --header "X-Vault-Token: ..." \ + http://127.0.0.1:8200/v1/sys/config/ui/headers/X-Custom-Header?multivalue=true +``` + +### Sample Response + +```json +{ + "values": ["custom-value-1", "custom-value-2"] +} +``` + ## Configure UI Headers This endpoint allows configuring the values to be returned for the UI header.