http: audit endpoints

This commit is contained in:
Mitchell Hashimoto
2015-04-01 18:36:13 -07:00
parent a8390aa2db
commit 47d52be3ae
4 changed files with 200 additions and 0 deletions

View File

@@ -26,6 +26,8 @@ func Handler(core *vault.Core) http.Handler {
mux.Handle("/v1/sys/revoke/", handleSysRevoke(core))
mux.Handle("/v1/sys/revoke-prefix/", handleSysRevokePrefix(core))
mux.Handle("/v1/sys/auth/", handleSysAuth(core))
mux.Handle("/v1/sys/audit", handleSysListAudit(core))
mux.Handle("/v1/sys/audit/", handleSysAudit(core))
mux.Handle("/v1/", handleLogical(core))
return mux
}

113
http/sys_audit.go Normal file
View File

@@ -0,0 +1,113 @@
package http
import (
"net/http"
"strings"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
)
func handleSysListAudit(core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
respondError(w, http.StatusMethodNotAllowed, nil)
return
}
resp, err := core.HandleRequest(requestAuth(r, &logical.Request{
Operation: logical.ReadOperation,
Path: "sys/audit",
}))
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
respondOk(w, resp.Data)
})
}
func handleSysAudit(core *vault.Core) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
fallthrough
case "PUT":
handleSysEnableAudit(core, w, r)
case "DELETE":
handleSysDisableAudit(core, w, r)
default:
respondError(w, http.StatusMethodNotAllowed, nil)
return
}
})
}
func handleSysDisableAudit(core *vault.Core, w http.ResponseWriter, r *http.Request) {
// Determine the path...
prefix := "/v1/sys/audit/"
if !strings.HasPrefix(r.URL.Path, prefix) {
respondError(w, http.StatusNotFound, nil)
return
}
path := r.URL.Path[len(prefix):]
if path == "" {
respondError(w, http.StatusNotFound, nil)
return
}
_, err := core.HandleRequest(requestAuth(r, &logical.Request{
Operation: logical.DeleteOperation,
Path: "sys/audit/" + path,
}))
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
respondOk(w, nil)
}
func handleSysEnableAudit(core *vault.Core, w http.ResponseWriter, r *http.Request) {
// Determine the path...
prefix := "/v1/sys/audit/"
if !strings.HasPrefix(r.URL.Path, prefix) {
respondError(w, http.StatusNotFound, nil)
return
}
path := r.URL.Path[len(prefix):]
if path == "" {
respondError(w, http.StatusNotFound, nil)
return
}
// Parse the request if we can
var req enableAuditRequest
if err := parseRequest(r, &req); err != nil {
respondError(w, http.StatusBadRequest, err)
return
}
_, err := core.HandleRequest(requestAuth(r, &logical.Request{
Operation: logical.WriteOperation,
Path: "sys/audit/" + path,
Data: map[string]interface{}{
"type": req.Type,
"description": req.Description,
"options": req.Options,
},
}))
if err != nil {
respondError(w, http.StatusInternalServerError, err)
return
}
respondOk(w, nil)
}
type enableAuditRequest struct {
Type string `json:"type"`
Description string `json:"description"`
Options map[string]string `json:"options"`
}

68
http/sys_audit_test.go Normal file
View File

@@ -0,0 +1,68 @@
package http
import (
"net/http"
"reflect"
"testing"
"github.com/hashicorp/vault/vault"
)
func TestSysAudit(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()
TestServerAuth(t, addr, token)
resp := testHttpPost(t, addr+"/v1/sys/audit/noop", map[string]interface{}{
"type": "noop",
})
testResponseStatus(t, resp, 204)
resp, err := http.Get(addr + "/v1/sys/audit")
if err != nil {
t.Fatalf("err: %s", err)
}
var actual map[string]interface{}
expected := map[string]interface{}{
"noop": map[string]interface{}{
"type": "noop",
"description": "",
"options": map[string]interface{}{},
},
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
func TestSysDisableAudit(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
defer ln.Close()
TestServerAuth(t, addr, token)
resp := testHttpPost(t, addr+"/v1/sys/audit/foo", map[string]interface{}{
"type": "noop",
})
testResponseStatus(t, resp, 204)
resp = testHttpDelete(t, addr+"/v1/sys/audit/foo")
testResponseStatus(t, resp, 204)
resp, err := http.Get(addr + "/v1/sys/audit")
if err != nil {
t.Fatalf("err: %s", err)
}
var actual map[string]interface{}
expected := map[string]interface{}{}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}

View File

@@ -3,6 +3,7 @@ package vault
import (
"testing"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"github.com/hashicorp/vault/physical"
@@ -13,6 +14,11 @@ import (
// TestCore returns a pure in-memory, uninitialized core for testing.
func TestCore(t *testing.T) *Core {
noopAudits := map[string]audit.Factory{
"noop": func(map[string]string) (audit.Backend, error) {
return new(noopAudit), nil
},
}
noopBackends := make(map[string]logical.Factory)
noopBackends["noop"] = func(map[string]string) (logical.Backend, error) {
return new(framework.Backend), nil
@@ -21,6 +27,7 @@ func TestCore(t *testing.T) *Core {
physicalBackend := physical.NewInmem()
c, err := NewCore(&CoreConfig{
Physical: physicalBackend,
AuditBackends: noopAudits,
LogicalBackends: noopBackends,
CredentialBackends: noopBackends,
})
@@ -71,3 +78,13 @@ func TestKeyCopy(key []byte) []byte {
copy(result, key)
return result
}
type noopAudit struct{}
func (n *noopAudit) LogRequest(a *logical.Auth, r *logical.Request) error {
return nil
}
func (n *noopAudit) LogResponse(a *logical.Auth, r *logical.Request, re *logical.Response, err error) error {
return nil
}