audit: separate hashing from formatting to facilitate raw

This commit is contained in:
Mitchell Hashimoto 2015-04-22 07:41:53 +02:00
parent dd3a6bf37f
commit 1b34aae7f1
4 changed files with 105 additions and 47 deletions

View File

@ -2,7 +2,6 @@ package audit
import (
"encoding/json"
"fmt"
"io"
"github.com/hashicorp/vault/logical"
@ -20,16 +19,6 @@ func (f *FormatJSON) FormatRequest(
auth = new(logical.Auth)
}
// Hash the data
dataRaw, err := HashStructure(req.Data, f.hashFunc())
if err != nil {
return err
}
data, ok := dataRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("data came back as not map")
}
// Encode!
enc := json.NewEncoder(w)
return enc.Encode(&JSONRequestEntry{
@ -43,7 +32,7 @@ func (f *FormatJSON) FormatRequest(
Request: JSONRequest{
Operation: req.Operation,
Path: req.Path,
Data: data,
Data: req.Data,
},
})
}
@ -54,8 +43,6 @@ func (f *FormatJSON) FormatResponse(
req *logical.Request,
resp *logical.Response,
err error) error {
hashFunc := f.hashFunc()
// If things are nil, make empty to avoid panics
if auth == nil {
auth = new(logical.Auth)
@ -66,13 +53,8 @@ func (f *FormatJSON) FormatResponse(
var respAuth JSONAuth
if resp.Auth != nil {
token, err := hashFunc(resp.Auth.ClientToken)
if err != nil {
return err
}
respAuth = JSONAuth{
ClientToken: token,
ClientToken: resp.Auth.ClientToken,
Policies: resp.Auth.Policies,
Metadata: resp.Auth.Metadata,
}
@ -85,25 +67,6 @@ func (f *FormatJSON) FormatResponse(
}
}
// Hash the data
dataRaw, err := HashStructure(req.Data, f.hashFunc())
if err != nil {
return err
}
reqData, ok := dataRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("data came back as not map")
}
dataRaw, err = HashStructure(resp.Data, f.hashFunc())
if err != nil {
return err
}
respData, ok := dataRaw.(map[string]interface{})
if !ok {
return fmt.Errorf("data came back as not map")
}
// Encode!
enc := json.NewEncoder(w)
return enc.Encode(&JSONResponseEntry{
@ -117,22 +80,18 @@ func (f *FormatJSON) FormatResponse(
Request: JSONRequest{
Operation: req.Operation,
Path: req.Path,
Data: reqData,
Data: req.Data,
},
Response: JSONResponse{
Auth: respAuth,
Secret: respSecret,
Data: respData,
Data: resp.Data,
Redirect: resp.Redirect,
},
})
}
func (f *FormatJSON) hashFunc() HashCallback {
return HashSHA1("")
}
// JSONRequest is the structure of a request audit log entry in JSON.
type JSONRequestEntry struct {
Type string `json:"type"`

View File

@ -38,5 +38,5 @@ func TestFormatJSON_formatRequest(t *testing.T) {
}
}
const testFormatJSONReqBasicStr = `{"type":"request","auth":{"policies":["root"],"metadata":null},"request":{"operation":"write","path":"/foo","data":{}}}
const testFormatJSONReqBasicStr = `{"type":"request","auth":{"policies":["root"],"metadata":null},"request":{"operation":"write","path":"/foo","data":null}}
`

View File

@ -7,11 +7,61 @@ import (
"reflect"
"strings"
"github.com/hashicorp/vault/logical"
"github.com/mitchellh/copystructure"
"github.com/mitchellh/reflectwalk"
)
// HashStructures takes an interface and hashes all the values within
// Hash will hash the given type. This has built-in support for auth,
// requests, and responses. If it is a type that isn't recognized, then
// it will be passed through.
//
// The structure is modified in-place.
func Hash(raw interface{}) error {
fn := HashSHA1("")
switch s := raw.(type) {
case *logical.Auth:
if s.ClientToken != "" {
token, err := fn(s.ClientToken)
if err != nil {
return err
}
s.ClientToken = token
}
case *logical.Request:
if s.Auth != nil {
if err := Hash(s.Auth); err != nil {
return err
}
}
data, err := HashStructure(s.Data, fn)
if err != nil {
return err
}
s.Data = data.(map[string]interface{})
case *logical.Response:
if s.Auth != nil {
if err := Hash(s.Auth); err != nil {
return err
}
}
data, err := HashStructure(s.Data, fn)
if err != nil {
return err
}
s.Data = data.(map[string]interface{})
}
return nil
}
// HashStructure takes an interface and hashes all the values within
// the structure. Only _values_ are hashed: keys of objects are not.
//
// For the HashCallback, see the built-in HashCallbacks below.

View File

@ -1,10 +1,59 @@
package audit
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/vault/logical"
)
func TestHash(t *testing.T) {
cases := []struct {
Input interface{}
Output interface{}
}{
{
&logical.Auth{ClientToken: "foo"},
&logical.Auth{ClientToken: "sha1:0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"},
},
{
&logical.Request{
Data: map[string]interface{}{
"foo": "bar",
},
},
&logical.Request{
Data: map[string]interface{}{
"foo": "sha1:62cdb7020ff920e5aa642c3d4066950dd1f01f4d",
},
},
},
{
&logical.Response{
Data: map[string]interface{}{
"foo": "bar",
},
},
&logical.Response{
Data: map[string]interface{}{
"foo": "sha1:62cdb7020ff920e5aa642c3d4066950dd1f01f4d",
},
},
},
}
for _, tc := range cases {
input := fmt.Sprintf("%#v", tc.Input)
if err := Hash(tc.Input); err != nil {
t.Fatalf("err: %s\n\n%s", err, input)
}
if !reflect.DeepEqual(tc.Input, tc.Output) {
t.Fatalf("bad:\n\n%s\n\n%#v\n\n%#v", input, tc.Input, tc.Output)
}
}
}
func TestHashWalker(t *testing.T) {
replaceText := "foo"