http: generic read/write endpoint for secrets
This commit is contained in:
parent
ca358f64dd
commit
742923452b
|
@ -14,7 +14,7 @@ type Secret struct {
|
|||
Renewable bool
|
||||
LeaseDuration int `mapstructure:"lease_duration"`
|
||||
LeaseDurationMax int `mapstructure:"lease_duration_max"`
|
||||
Data map[string]interface{} `mapstructure:"-"`
|
||||
Data map[string]interface{} `mapstructure:"data"`
|
||||
}
|
||||
|
||||
// ParseSecret is used to parse a secret value from JSON from an io.Reader.
|
||||
|
|
|
@ -15,6 +15,7 @@ func Handler(core *vault.Core) http.Handler {
|
|||
mux.Handle("/v1/sys/seal-status", handleSysSealStatus(core))
|
||||
mux.Handle("/v1/sys/seal", handleSysSeal(core))
|
||||
mux.Handle("/v1/sys/unseal", handleSysUnseal(core))
|
||||
mux.Handle("/v1/", handleLogical(core))
|
||||
return mux
|
||||
}
|
||||
|
||||
|
|
80
http/logical.go
Normal file
80
http/logical.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func handleLogical(core *vault.Core) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Determine the path...
|
||||
if !strings.HasPrefix(r.URL.Path, "/v1/") {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
path := r.URL.Path[len("/v1/"):]
|
||||
if path == "" {
|
||||
respondError(w, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Determine the operation
|
||||
var op logical.Operation
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
op = logical.ReadOperation
|
||||
case "PUT":
|
||||
op = logical.WriteOperation
|
||||
default:
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the request if we can
|
||||
var req map[string]interface{}
|
||||
if op == logical.WriteOperation {
|
||||
if err := parseRequest(r, &req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Make the internal request
|
||||
resp, err := core.HandleRequest(&logical.Request{
|
||||
Operation: op,
|
||||
Path: path,
|
||||
Data: req,
|
||||
})
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
var httpResp interface{}
|
||||
if resp != nil {
|
||||
logicalResp := &LogicalResponse{Data: resp.Data}
|
||||
if resp.IsSecret && resp.Lease != nil {
|
||||
logicalResp.VaultId = resp.Lease.VaultID
|
||||
logicalResp.Renewable = resp.Lease.Renewable
|
||||
logicalResp.LeaseDuration = int(resp.Lease.Duration.Seconds())
|
||||
logicalResp.LeaseDurationMax = int(resp.Lease.MaxDuration.Seconds())
|
||||
}
|
||||
|
||||
httpResp = logicalResp
|
||||
}
|
||||
|
||||
// Respond
|
||||
respondOk(w, httpResp)
|
||||
})
|
||||
}
|
||||
|
||||
type LogicalResponse struct {
|
||||
VaultId string `json:"vault_id"`
|
||||
Renewable bool `json:"renewable"`
|
||||
LeaseDuration int `json:"lease_duration"`
|
||||
LeaseDurationMax int `json:"lease_duration_max"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
}
|
41
http/logical_test.go
Normal file
41
http/logical_test.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func TestLogical(t *testing.T) {
|
||||
core, _ := vault.TestCoreUnsealed(t)
|
||||
ln, addr := TestServer(t, core)
|
||||
defer ln.Close()
|
||||
|
||||
resp := testHttpPut(t, addr+"/v1/secret/foo", map[string]interface{}{
|
||||
"data": "bar",
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
resp, err := http.Get(addr + "/v1/secret/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
var actual map[string]interface{}
|
||||
expected := map[string]interface{}{
|
||||
"vault_id": "",
|
||||
"renewable": false,
|
||||
"lease_duration": float64(0),
|
||||
"lease_duration_max": float64(0),
|
||||
"data": map[string]interface{}{
|
||||
"data": "bar",
|
||||
},
|
||||
}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &actual)
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue