Skip to content

Commit

Permalink
Merge branch 'main' into remove-duplicate-policies-on-creating-entities
Browse files Browse the repository at this point in the history
  • Loading branch information
hghaf099 committed Oct 14, 2021
2 parents 1e635cd + c4e1a58 commit e8da9be
Show file tree
Hide file tree
Showing 75 changed files with 3,063 additions and 35,502 deletions.
Binary file added HCPV_badge.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions api/api_test.go
Expand Up @@ -9,8 +9,7 @@ import (

// testHTTPServer creates a test HTTP server that handles requests until
// the listener returned is closed.
func testHTTPServer(
t *testing.T, handler http.Handler) (*Config, net.Listener) {
func testHTTPServer(t *testing.T, handler http.Handler) (*Config, net.Listener) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("err: %s", err)
Expand Down
24 changes: 19 additions & 5 deletions api/logical.go
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"

Expand Down Expand Up @@ -130,24 +131,37 @@ func (c *Logical) List(path string) (*Secret, error) {
}

func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) {
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()

r := c.c.NewRequest("PUT", "/v1/"+path)
if err := r.SetJSONBody(data); err != nil {
return nil, err
}

return c.write(path, r)
return c.write(ctx, path, r)
}

func (c *Logical) JSONMergePatch(ctx context.Context, path string, data map[string]interface{}) (*Secret, error) {
r := c.c.NewRequest("PATCH", "/v1/"+path)
r.Headers = http.Header{
"Content-Type": []string{"application/merge-patch+json"},
}
if err := r.SetJSONBody(data); err != nil {
return nil, err
}

return c.write(ctx, path, r)
}

func (c *Logical) WriteBytes(path string, data []byte) (*Secret, error) {
r := c.c.NewRequest("PUT", "/v1/"+path)
r.BodyBytes = data

return c.write(path, r)
return c.write(context.Background(), path, r)
}

func (c *Logical) write(path string, request *Request) (*Secret, error) {
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
func (c *Logical) write(ctx context.Context, path string, request *Request) (*Secret, error) {
resp, err := c.c.RawRequestWithContext(ctx, request)
if resp != nil {
defer resp.Body.Close()
Expand Down
3 changes: 3 additions & 0 deletions changelog/12485.txt
@@ -0,0 +1,3 @@
```release-note:feature
**Customizable HTTP Headers**: Add support to define custom HTTP headers for root path (`/`) and also on API endpoints (`/v1/*`)
```
3 changes: 3 additions & 0 deletions changelog/12668.txt
@@ -0,0 +1,3 @@
```release-note:improvement
sdk/framework: The '+' wildcard is now supported for parameterizing unauthenticated paths.
```
5 changes: 5 additions & 0 deletions changelog/12687.txt
@@ -0,0 +1,5 @@
```release-note:feature
**KV patch**: Add partial update support the for the `/<mount>/data/:path` kv-v2
endpoint through HTTP `PATCH`. A new `patch` ACL capability has been added and
is required to make such requests.
```
3 changes: 3 additions & 0 deletions changelog/12800.txt
@@ -0,0 +1,3 @@
```release-note:feature
**OIDC Authorization Code Flow**: The Vault UI now supports OIDC Authorization Code Flow
```
7 changes: 7 additions & 0 deletions command/agent/config/config.go
Expand Up @@ -35,6 +35,7 @@ func (c *Config) Prune() {
l.RawConfig = nil
l.Profiling.UnusedKeys = nil
l.Telemetry.UnusedKeys = nil
l.CustomResponseHeaders = nil
}
c.FoundKeys = nil
c.UnusedKeys = nil
Expand Down Expand Up @@ -172,6 +173,12 @@ func LoadConfig(path string) (*Config, error) {
if err != nil {
return nil, err
}

// Pruning custom headers for Agent for now
for _, ln := range sharedConfig.Listeners {
ln.CustomResponseHeaders = nil
}

result.SharedConfig = sharedConfig

list, ok := obj.Node.(*ast.ObjectList)
Expand Down
2 changes: 0 additions & 2 deletions command/agent/config/config_test.go
Expand Up @@ -536,7 +536,6 @@ func TestLoadConfigFile_AgentCache_PersistMissingType(t *testing.T) {
}

func TestLoadConfigFile_TemplateConfig(t *testing.T) {

testCases := map[string]struct {
fixturePath string
expectedTemplateConfig TemplateConfig
Expand Down Expand Up @@ -586,7 +585,6 @@ func TestLoadConfigFile_TemplateConfig(t *testing.T) {
}
})
}

}

// TestLoadConfigFile_Template tests template definitions in Vault Agent
Expand Down
133 changes: 120 additions & 13 deletions command/kv_patch.go
@@ -1,11 +1,13 @@
package command

import (
"context"
"fmt"
"io"
"os"
"strings"

"github.com/hashicorp/vault/api"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
Expand All @@ -18,7 +20,9 @@ var (
type KVPatchCommand struct {
*BaseCommand

testStdin io.Reader // for tests
flagCAS int
flagMethod string
testStdin io.Reader // for tests
}

func (c *KVPatchCommand) Synopsis() string {
Expand All @@ -45,6 +49,25 @@ Usage: vault kv patch [options] KEY [DATA]
$ echo "abcd1234" | vault kv patch secret/foo bar=-
To perform a Check-And-Set operation, specify the -cas flag with the
appropriate version number corresponding to the key you want to perform
the CAS operation on:
$ vault kv patch -cas=1 secret/foo bar=baz
By default, this operation will attempt an HTTP PATCH operation. If your
policy does not allow that, it will fall back to a read/local update/write approach.
If you wish to specify which method this command should use, you may do so
with the -method flag. When -method=patch is specified, only an HTTP PATCH
operation will be tried. If it fails, the entire command will fail.
$ vault kv patch -method=patch secret/foo bar=baz
When -method=rw is specified, only a read/local update/write approach will be tried.
This was the default behavior previous to Vault 1.9.
$ vault kv patch -method=rw secret/foo bar=baz
Additional flags and more advanced use cases are detailed below.
` + c.Flags().Help()
Expand All @@ -54,6 +77,27 @@ Usage: vault kv patch [options] KEY [DATA]
func (c *KVPatchCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)

// Patch specific options
f := set.NewFlagSet("Common Options")

f.IntVar(&IntVar{
Name: "cas",
Target: &c.flagCAS,
Default: 0,
Usage: `Specifies to use a Check-And-Set operation. If set to 0 or not
set, the patch will be allowed. If the index is non-zero the patch will
only be allowed if the key’s current version matches the version
specified in the cas parameter.`,
})

f.StringVar(&StringVar{
Name: "method",
Target: &c.flagMethod,
Usage: `Specifies which method of patching to use. If set to "patch", then
an HTTP PATCH request will be issued. If set to "rw", then a read will be
performed, then a local update, followed by a remote update.`,
})

return set
}

Expand Down Expand Up @@ -121,6 +165,30 @@ func (c *KVPatchCommand) Run(args []string) int {
return 2
}

// Check the method and behave accordingly
var secret *api.Secret
var code int

switch c.flagMethod {
case "rw":
secret, code = c.readThenWrite(client, path, newData)
case "patch":
secret, code = c.mergePatch(client, path, newData, false)
case "":
secret, code = c.mergePatch(client, path, newData, true)
default:
c.UI.Error(fmt.Sprintf("Unsupported method provided to -method flag: %s", c.flagMethod))
return 2
}

if code != 0 {
return code
}

return OutputSecret(c.UI, secret)
}

func (c *KVPatchCommand) readThenWrite(client *api.Client, path string, newData map[string]interface{}) (*api.Secret, int) {
// First, do a read.
// Note that we don't want to see curl output for the read request.
curOutputCurl := client.OutputCurlString()
Expand All @@ -129,45 +197,45 @@ func (c *KVPatchCommand) Run(args []string) int {
client.SetOutputCurlString(curOutputCurl)
if err != nil {
c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", path, err))
return 2
return nil, 2
}

// Make sure a value already exists
if secret == nil || secret.Data == nil {
c.UI.Error(fmt.Sprintf("No value found at %s", path))
return 2
return nil, 2
}

// Verify metadata found
rawMeta, ok := secret.Data["metadata"]
if !ok || rawMeta == nil {
c.UI.Error(fmt.Sprintf("No metadata found at %s; patch only works on existing data", path))
return 2
return nil, 2
}
meta, ok := rawMeta.(map[string]interface{})
if !ok {
c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", path))
return 2
return nil, 2
}
if meta == nil {
c.UI.Error(fmt.Sprintf("No metadata found at %s; patch only works on existing data", path))
return 2
return nil, 2
}

// Verify old data found
rawData, ok := secret.Data["data"]
if !ok || rawData == nil {
c.UI.Error(fmt.Sprintf("No data found at %s; patch only works on existing data", path))
return 2
return nil, 2
}
data, ok := rawData.(map[string]interface{})
if !ok {
c.UI.Error(fmt.Sprintf("Data found at %s is not the expected type (JSON object)", path))
return 2
return nil, 2
}
if data == nil {
c.UI.Error(fmt.Sprintf("No data found at %s; patch only works on existing data", path))
return 2
return nil, 2
}

// Copy new data over
Expand All @@ -183,19 +251,58 @@ func (c *KVPatchCommand) Run(args []string) int {
})
if err != nil {
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
return 2
return nil, 2
}

if secret == nil {
// Don't output anything unless using the "table" format
if Format(c.UI) == "table" {
c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
}
return 0
return nil, 0
}

if c.flagField != "" {
return PrintRawField(c.UI, secret, c.flagField)
return nil, PrintRawField(c.UI, secret, c.flagField)
}

return OutputSecret(c.UI, secret)
return secret, 0
}

func (c *KVPatchCommand) mergePatch(client *api.Client, path string, newData map[string]interface{}, rwFallback bool) (*api.Secret, int) {
data := map[string]interface{}{
"data": newData,
"options": map[string]interface{}{},
}

if c.flagCAS > 0 {
data["options"].(map[string]interface{})["cas"] = c.flagCAS
}

secret, err := client.Logical().JSONMergePatch(context.Background(), path, data)
if err != nil {
// If it's a 403, that probably means they don't have the patch capability in their policy. Fall back to
// the old way of doing it if the user didn't specify a -method. If they did, and it was "patch", then just error.
if re, ok := err.(*api.ResponseError); ok && re.StatusCode == 403 && rwFallback {
c.UI.Warn(fmt.Sprintf("Data was written to %s but we recommend that you add the \"patch\" capability to your ACL policy in order to use HTTP PATCH in the future.", path))
return c.readThenWrite(client, path, newData)
}

c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
return nil, 2
}

if secret == nil {
// Don't output anything unless using the "table" format
if Format(c.UI) == "table" {
c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
}
return nil, 0
}

if c.flagField != "" {
return nil, PrintRawField(c.UI, secret, c.flagField)
}

return secret, 0
}

0 comments on commit e8da9be

Please sign in to comment.