diff --git a/http/logical.go b/http/logical.go index 2b09414e54..2d09f67e51 100644 --- a/http/logical.go +++ b/http/logical.go @@ -75,6 +75,30 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques } } + // If we are a read operation, try and parse any parameters + if op == logical.ReadOperation { + getData := map[string]interface{}{} + + for k, v := range r.URL.Query() { + // Skip the help key as this is a reserved parameter + if k == "help" { + continue + } + + switch { + case len(v) == 0: + case len(v) == 1: + getData[k] = v[0] + default: + getData[k] = v + } + } + + if len(getData) > 0 { + data = getData + } + } + var err error request_id, err := uuid.GenerateUUID() if err != nil { diff --git a/http/plugin_test.go b/http/plugin_test.go index b96e8d4840..b5c978992a 100644 --- a/http/plugin_test.go +++ b/http/plugin_test.go @@ -1,8 +1,10 @@ package http import ( + "encoding/json" "io/ioutil" "os" + "reflect" "sync" "testing" @@ -96,7 +98,7 @@ func TestPlugin_MockList(t *testing.T) { defer cluster.Cleanup() _, err := core.Client.Logical().Write("mock/kv/foo", map[string]interface{}{ - "bar": "baz", + "value": "baz", }) if err != nil { t.Fatal(err) @@ -111,7 +113,7 @@ func TestPlugin_MockList(t *testing.T) { } _, err = core.Client.Logical().Write("mock/kv/zoo", map[string]interface{}{ - "bar": "baz", + "value": "baz", }) if err != nil { t.Fatal(err) @@ -151,3 +153,40 @@ func TestPlugin_MockRawResponse(t *testing.T) { } } + +func TestPlugin_GetParams(t *testing.T) { + logger := logbridge.NewLogger(hclog.New(&hclog.LoggerOptions{ + Mutex: &sync.Mutex{}, + })) + cluster, core := getPluginClusterAndCore(t, logger) + defer cluster.Cleanup() + + _, err := core.Client.Logical().Write("mock/kv/foo", map[string]interface{}{ + "value": "baz", + }) + if err != nil { + t.Fatal(err) + } + + r := core.Client.NewRequest("GET", "/v1/mock/kv/foo") + r.Params.Add("version", "12") + resp, err := core.Client.RawRequest(r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + secret, err := api.ParseSecret(resp.Body) + if err != nil { + t.Fatal(err) + } + + expected := map[string]interface{}{ + "value": "baz", + "version": json.Number("12"), + } + + if !reflect.DeepEqual(secret.Data, expected) { + t.Fatal(secret.Data) + } +} diff --git a/logical/plugin/mock/path_kv.go b/logical/plugin/mock/path_kv.go index 7efa495a0c..3e599621ed 100644 --- a/logical/plugin/mock/path_kv.go +++ b/logical/plugin/mock/path_kv.go @@ -21,8 +21,9 @@ func kvPaths(b *backend) []*framework.Path { &framework.Path{ Pattern: "kv/" + framework.GenericNameRegex("key"), Fields: map[string]*framework.FieldSchema{ - "key": &framework.FieldSchema{Type: framework.TypeString}, - "value": &framework.FieldSchema{Type: framework.TypeString}, + "key": &framework.FieldSchema{Type: framework.TypeString}, + "value": &framework.FieldSchema{Type: framework.TypeString}, + "version": &framework.FieldSchema{Type: framework.TypeInt}, }, ExistenceCheck: b.pathExistenceCheck, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -45,6 +46,8 @@ func (b *backend) pathExistenceCheck(ctx context.Context, req *logical.Request, } func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + version := data.Get("version").(int) + entry, err := req.Storage.Get(ctx, req.Path) if err != nil { return nil, err @@ -58,11 +61,16 @@ func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *fr b.Logger().Info("reading value", "key", req.Path, "value", value) // Return the secret - return &logical.Response{ + resp := &logical.Response{ Data: map[string]interface{}{ - "value": value, + "value": value, + "version": version, }, - }, nil + } + if version != 0 { + resp.Data["version"] = version + } + return resp, nil } func (b *backend) pathKVCreateUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {