diff --git a/audit/format_json.go b/audit/format_json.go new file mode 100644 index 0000000000..6bfbb3624f --- /dev/null +++ b/audit/format_json.go @@ -0,0 +1,126 @@ +package audit + +import ( + "encoding/json" + "io" + + "github.com/hashicorp/vault/logical" +) + +// FormatJSON is a Formatter implementation that structuteres data into +// a JSON format. +type FormatJSON struct{} + +func (f *FormatJSON) FormatRequest( + w io.Writer, + auth *logical.Auth, req *logical.Request) error { + // If auth is nil, make an empty one + if auth == nil { + auth = new(logical.Auth) + } + + // Encode! + enc := json.NewEncoder(w) + return enc.Encode(&JSONRequestEntry{ + Auth: JSONAuth{ + Policies: auth.Policies, + Metadata: auth.Metadata, + }, + + Request: JSONRequest{ + Operation: req.Operation, + Path: req.Path, + Data: req.Data, + }, + }) +} + +func (f *FormatJSON) FormatResponse( + w io.Writer, + auth *logical.Auth, + req *logical.Request, + resp *logical.Response, + err error) error { + // If things are nil, make empty to avoid panics + if auth == nil { + auth = new(logical.Auth) + } + if resp == nil { + resp = new(logical.Response) + } + + var respAuth JSONAuth + if resp.Auth != nil { + respAuth = JSONAuth{ + ClientToken: resp.Auth.ClientToken, + Policies: resp.Auth.Policies, + Metadata: resp.Auth.Metadata, + } + } + + var respSecret JSONSecret + if resp.Secret != nil { + respSecret = JSONSecret{ + LeaseID: resp.Secret.LeaseID, + } + } + + // Encode! + enc := json.NewEncoder(w) + return enc.Encode(&JSONResponseEntry{ + Auth: JSONAuth{ + Policies: auth.Policies, + Metadata: auth.Metadata, + }, + + Request: JSONRequest{ + Operation: req.Operation, + Path: req.Path, + Data: req.Data, + }, + + Response: JSONResponse{ + Auth: respAuth, + Secret: respSecret, + Data: resp.Data, + Redirect: resp.Redirect, + }, + }) +} + +// JSONRequest is the structure of a request audit log entry in JSON. +type JSONRequestEntry struct { + Auth JSONAuth `json:"auth"` + Request JSONRequest `json:"request"` +} + +// JSONResponseEntry is the structure of a response audit log entry in JSON. +type JSONResponseEntry struct { + Error string `json:"error"` + Auth JSONAuth `json:"auth"` + Request JSONRequest `json:"request"` + Response JSONResponse `json:"request"` +} + +type JSONRequest struct { + Operation logical.Operation `json:"operation"` + Path string `json:"path"` + Data map[string]interface{} `json:"data"` +} + +type JSONResponse struct { + Auth JSONAuth `json:"auth,omitempty"` + Secret JSONSecret `json:"secret,emitempty"` + Data map[string]interface{} `json:"data"` + Redirect string `json:"redirect"` +} + +type JSONAuth struct { + ClientToken string `json:"string,omitempty"` + Policies []string `json:"policies"` + Metadata map[string]string `json:"metadata"` +} + +type JSONSecret struct { + LeaseID string `json:"lease_id"` +} diff --git a/audit/format_json_test.go b/audit/format_json_test.go new file mode 100644 index 0000000000..2fca3a900b --- /dev/null +++ b/audit/format_json_test.go @@ -0,0 +1,42 @@ +package audit + +import ( + "bytes" + "testing" + + "github.com/hashicorp/vault/logical" +) + +func TestFormatJSON_formatRequest(t *testing.T) { + cases := map[string]struct { + Auth *logical.Auth + Req *logical.Request + Result string + }{ + "auth, request": { + &logical.Auth{ClientToken: "foo", Policies: []string{"root"}}, + &logical.Request{ + Operation: logical.WriteOperation, + Path: "/foo", + }, + testFormatJSONReqBasicStr, + }, + } + + for name, tc := range cases { + var buf bytes.Buffer + var format FormatJSON + if err := format.FormatRequest(&buf, tc.Auth, tc.Req); err != nil { + t.Fatalf("bad: %s\nerr: %s", name, err) + } + + if buf.String() != tc.Result { + t.Fatalf( + "bad: %s\nResult:\n\n%s\n\nExpected:\n\n%s", + name, buf.String(), tc.Result) + } + } +} + +const testFormatJSONReqBasicStr = `{"auth":{"policies":["root"],"metadata":null},"request":{"operation":"write","path":"/foo","data":null}} +` diff --git a/audit/formatter.go b/audit/formatter.go new file mode 100644 index 0000000000..c1f5597f1d --- /dev/null +++ b/audit/formatter.go @@ -0,0 +1,15 @@ +package audit + +import ( + "io" + + "github.com/hashicorp/vault/logical" +) + +// Formatter is an interface that is responsible for formating a +// request/response into some format. Formatters write their output +// to an io.Writer. +type Formatter interface { + FormatRequest(io.Writer, *logical.Auth, *logical.Request) error + FormatResponse(io.Writer, *logical.Auth, *logical.Request, *logical.Response, error) error +}