diff options
author | Kamil Kisiel <kamil@kamilkisiel.net> | 2015-06-06 01:49:47 +0300 |
---|---|---|
committer | Kamil Kisiel <kamil@kamilkisiel.net> | 2015-06-06 01:49:47 +0300 |
commit | 203fca9e226add43f54aa91a8b7c2836a52803ce (patch) | |
tree | 96acf4ed01e065a641eb53c1bd13e79651579fb2 | |
parent | 8e98dd730fc43f1383f19615db6f2e702c9738e8 (diff) | |
parent | b002d4848c7e09324366937cf81e7bcad77e207a (diff) |
Merge pull request #21 from elithrar/json-encoder
Added support for encoding/json
-rw-r--r-- | securecookie.go | 61 | ||||
-rw-r--r-- | securecookie_test.go | 31 |
2 files changed, 82 insertions, 10 deletions
diff --git a/securecookie.go b/securecookie.go index 1b7acf8..cfee1d8 100644 --- a/securecookie.go +++ b/securecookie.go @@ -14,6 +14,7 @@ import ( "crypto/subtle" "encoding/base64" "encoding/gob" + "encoding/json" "errors" "fmt" "hash" @@ -44,6 +45,7 @@ type Codec interface { // GenerateRandomKey(). The key length must correspond to the block size // of the encryption algorithm. For AES, used by default, valid lengths are // 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. +// The default encoder used for cookie serialization is encoding/gob. func New(hashKey, blockKey []byte) *SecureCookie { s := &SecureCookie{ hashKey: hashKey, @@ -51,6 +53,7 @@ func New(hashKey, blockKey []byte) *SecureCookie { hashFunc: sha256.New, maxAge: 86400 * 30, maxLength: 4096, + sz: GobEncoder{}, } if hashKey == nil { s.err = errHashKeyNotSet @@ -72,11 +75,28 @@ type SecureCookie struct { maxAge int64 minAge int64 err error + sz Serializer // For testing purposes, the function that returns the current timestamp. // If not set, it will use time.Now().UTC().Unix(). timeFunc func() int64 } +// Serializer provides an interface for providing custom serializers for cookie +// values. +type Serializer interface { + Serialize(src interface{}) ([]byte, error) + Deserialize(src []byte, dst interface{}) error +} + +// GobEncoder encodes cookie values using encoding/gob. This is the simplest +// encoder and can handle complex types via gob.Register. +type GobEncoder struct{} + +// JSONEncoder encodes cookie values using encoding/json. Users who wish to +// encode complex types need to satisfy the json.Marshaller and +// json.Unmarshaller interfaces. +type JSONEncoder struct{} + // MaxLength restricts the maximum length, in bytes, for the cookie value. // // Default is 4096, which is the maximum value accepted by Internet Explorer. @@ -123,6 +143,15 @@ func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCo return s } +// Encoding sets the encoding/serialization method for cookies. +// +// Default is encoding/gob. +func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie { + s.sz = sz + + return s +} + // Encode encodes a cookie value. // // It serializes, optionally encrypts, signs with a message authentication code, and @@ -143,7 +172,7 @@ func (s *SecureCookie) Encode(name string, value interface{}) (string, error) { var err error var b []byte // 1. Serialize. - if b, err = serialize(value); err != nil { + if b, err = s.sz.Serialize(value); err != nil { return "", err } // 2. Encrypt (optional). @@ -226,7 +255,7 @@ func (s *SecureCookie) Decode(name, value string, dst interface{}) error { } } // 6. Deserialize. - if err = deserialize(b, dst); err != nil { + if err = s.sz.Deserialize(b, dst); err != nil { return err } // Done. @@ -300,8 +329,8 @@ func decrypt(block cipher.Block, value []byte) ([]byte, error) { // Serialization -------------------------------------------------------------- -// serialize encodes a value using gob. -func serialize(src interface{}) ([]byte, error) { +// Serialize encodes a value using gob. +func (e GobEncoder) Serialize(src interface{}) ([]byte, error) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) if err := enc.Encode(src); err != nil { @@ -310,8 +339,8 @@ func serialize(src interface{}) ([]byte, error) { return buf.Bytes(), nil } -// deserialize decodes a value using gob. -func deserialize(src []byte, dst interface{}) error { +// Deserialize decodes a value using gob. +func (e GobEncoder) Deserialize(src []byte, dst interface{}) error { dec := gob.NewDecoder(bytes.NewBuffer(src)) if err := dec.Decode(dst); err != nil { return err @@ -319,6 +348,26 @@ func deserialize(src []byte, dst interface{}) error { return nil } +// Serialize encodes a value using encoding/json. +func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + if err := enc.Encode(src); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// Deserialize decodes a value using encoding/json. +func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error { + dec := json.NewDecoder(bytes.NewReader(src)) + if err := dec.Decode(dst); err != nil { + return err + } + return nil +} + // Encoding ------------------------------------------------------------------- // encode encodes a value using base64. diff --git a/securecookie_test.go b/securecookie_test.go index 241ff10..76368a9 100644 --- a/securecookie_test.go +++ b/securecookie_test.go @@ -101,7 +101,7 @@ func TestAuthentication(t *testing.T) { } } -func TestEncription(t *testing.T) { +func TestEncryption(t *testing.T) { block, err := aes.NewCipher([]byte("1234567890123456")) if err != nil { t.Fatalf("Block could not be created") @@ -121,18 +121,41 @@ func TestEncription(t *testing.T) { } } -func TestSerialization(t *testing.T) { +func TestGobSerialization(t *testing.T) { var ( + sz GobEncoder serialized []byte deserialized map[string]string err error ) for _, value := range testCookies { - if serialized, err = serialize(value); err != nil { + if serialized, err = sz.Serialize(value); err != nil { t.Error(err) } else { deserialized = make(map[string]string) - if err = deserialize(serialized, &deserialized); err != nil { + if err = sz.Deserialize(serialized, &deserialized); err != nil { + t.Error(err) + } + if fmt.Sprintf("%v", deserialized) != fmt.Sprintf("%v", value) { + t.Errorf("Expected %v, got %v.", value, deserialized) + } + } + } +} + +func TestJSONSerialization(t *testing.T) { + var ( + sz JSONEncoder + serialized []byte + deserialized map[string]string + err error + ) + for _, value := range testCookies { + if serialized, err = sz.Serialize(value); err != nil { + t.Error(err) + } else { + deserialized = make(map[string]string) + if err = sz.Deserialize(serialized, &deserialized); err != nil { t.Error(err) } if fmt.Sprintf("%v", deserialized) != fmt.Sprintf("%v", value) { |