From d188a7b75c35dacec656c431fc335452e061ff5b Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Tue, 13 Aug 2019 14:52:55 -0700 Subject: [PATCH 1/7] New: Add secrets/encoding package to standardize secret value encoding for storage To maintain secret value integrity, we need to encode the value bytes in a common encoding for storage. We also need to make it transparent to the user so we don't force them to encode their values before adding them to the secret store. This package also helps maintain backwards compatibility with the current system that doesn't have any consistent value encoding. --- README.md | 11 ++++ secrets/encoding/doc.go | 23 ++++++++ secrets/encoding/encoding.go | 90 +++++++++++++++++++++++++++++++ secrets/encoding/encoding_test.go | 63 ++++++++++++++++++++++ secrets/encoding/interface.go | 18 +++++++ 5 files changed, 205 insertions(+) create mode 100644 secrets/encoding/doc.go create mode 100644 secrets/encoding/encoding.go create mode 100644 secrets/encoding/encoding_test.go create mode 100644 secrets/encoding/interface.go diff --git a/README.md b/README.md index 29cf82d..c36b0a8 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,14 @@ The functionality in this package should work on Linux, MacOS and the BSDs. - add a mechanism for root interactions - add Windows support + +## secrets/encoding + +This package provides an interface to encode and decode secret values that will get stored +in the secret store. This is required to ensure that values are consistently stored safely, +but also doesn't enforce an encoding that users must use when storing secrets. The default +algorithm is base64 and all encoded strings generated will be prefixed with "base64:". If +there is no encoding prefix, there is `NoEncodingType` that will just return the original +value unencoded. + +Help can be found by running `go doc -all github.com/puppetlabs/horsehead/secrets/encoding`. diff --git a/secrets/encoding/doc.go b/secrets/encoding/doc.go new file mode 100644 index 0000000..d7939af --- /dev/null +++ b/secrets/encoding/doc.go @@ -0,0 +1,23 @@ +/* +Package encoding provides an interface for encoding and decoding secret +values for storage. The utility in this package is transparent to the user +and it is used to maintain byte integrity on secret values used in workflows. + +Basic use when encoding a value: + encoder := encoding.Encoders[encoding.DefaultEncodingType]() + + result, err := encoder.EncodeSecretValue([]byte("super secret token")) + if err != nil { + // handle error + } + +Basic use when decoding a value: + encodingType, value := encoding.ParseEncodedValue("base64:c3VwZXIgc2VjcmV0IHRva2Vu") + encoder := encoding.Encoders[encoderType]() + + result, err := encoder.DecodeSecretValue(value) + if err != nil { + // handle error + } +*/ +package encoding diff --git a/secrets/encoding/encoding.go b/secrets/encoding/encoding.go new file mode 100644 index 0000000..164fe11 --- /dev/null +++ b/secrets/encoding/encoding.go @@ -0,0 +1,90 @@ +package encoding + +import ( + "encoding/base64" + "fmt" + "strings" +) + +type encodingType string + +func (p encodingType) String() string { + return string(p) +} + +const ( + Base64EncodingType encodingType = "base64" + NoEncodingType encodingType = "" +) + +// DefaultEncodingType is the default encodingType. This makes it easier to use this +// package as the caller won't need to make any desisions around what encoder to use +// unless they really need to. +const DefaultEncodingType = Base64EncodingType + +var encodingTypeMap = map[string]encodingType{ + "base64": Base64EncodingType, + "": NoEncodingType, +} + +// ParseEncodedValue will attempt to split on : and extract an encoding identifer +// from the prefix of the string. It then returns the discovered encodingType and the +// value without the encodingType prefixed. +func ParseEncodedValue(value string) (encodingType, string) { + parts := strings.Split(value, ":") + + t, ok := encodingTypeMap[parts[0]] + if !ok { + return NoEncodingType, value + } + + return t, strings.Join(parts[1:], ":") +} + +// Encoders maps encoding algorithms to their respective EncodeDecoder types. +// Example: +// +// ed := encoding.Encoders[Base64EncodingType]() +// encodedValue, err := ed.EncodeSecretValue("my super secret value") +var Encoders = map[encodingType]func() EncodeDecoder{ + Base64EncodingType: func() EncodeDecoder { + return Base64Encoding{} + }, + NoEncodingType: func() EncodeDecoder { + return NoEncoding{} + }, +} + +// Base64Encoding handles the encoding and decoding of secret values using base64. +// All encoded values will be prefixed with "base64:" +type Base64Encoding struct{} + +// EncodeSecretValue takes a byte slice and returns it encoded as a base64 string. +// No error is ever returned. +func (e Base64Encoding) EncodeSecretValue(value []byte) (string, error) { + s := base64.StdEncoding.EncodeToString(value) + + return fmt.Sprintf("%s:%s", Base64EncodingType, s), nil +} + +// DecodeSecretValue takes a string and attempts to decode using a base64 decoder. +// If an error is returned, it will originate from the Go encoding/base64 package. +func (e Base64Encoding) DecodeSecretValue(value string) ([]byte, error) { + return base64.StdEncoding.DecodeString(value) +} + +// NoEncoding just returns the values without encoding them. This is used when there +// is no encoding type algorithm prefix on the value. +type NoEncoding struct{} + +// EncodeSecretValue takes a byte slice and casts it to a string. No error is ever +// returned. +func (e NoEncoding) EncodeSecretValue(value []byte) (string, error) { + return string(value), nil +} + +// DecodeSecretValue takes a string and casts it to a byte slice. No error is ever +// returned. +func (e NoEncoding) DecodeSecretValue(value string) ([]byte, error) { + return []byte(value), nil +} diff --git a/secrets/encoding/encoding_test.go b/secrets/encoding/encoding_test.go new file mode 100644 index 0000000..4ed9c32 --- /dev/null +++ b/secrets/encoding/encoding_test.go @@ -0,0 +1,63 @@ +package encoding + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEncoding(t *testing.T) { + var cases = []struct { + description string + value string + encodingType encodingType + expected string + }{ + { + description: "base64 encoding succeeds", + value: "super secret token", + encodingType: Base64EncodingType, + expected: "base64:c3VwZXIgc2VjcmV0IHRva2Vu", + }, + { + description: "no encoding succeeds", + value: "super secret token", + encodingType: NoEncodingType, + expected: "super secret token", + }, + { + description: "base64 complex values don't loose integrity", + value: "super: secret token:12:49:wheel", + encodingType: Base64EncodingType, + expected: "base64:c3VwZXI6IHNlY3JldCB0b2tlbjoxMjo0OTp3aGVlbA==", + }, + { + description: "no encoding complex values don't loose integrity", + value: "super: secret token:12:49:wheel", + encodingType: NoEncodingType, + expected: "super: secret token:12:49:wheel", + }, + } + + for _, c := range cases { + t.Run(c.description, func(t *testing.T) { + ed := Encoders[c.encodingType]() + + result, err := ed.EncodeSecretValue([]byte(c.value)) + require.NoError(t, err) + require.Equal(t, c.expected, result, fmt.Sprintf("result was malformed: %s", result)) + + typ, value := ParseEncodedValue(result) + require.Equal(t, c.encodingType, typ) + + newED := Encoders[typ]() + + { + newResult, err := newED.DecodeSecretValue(value) + require.NoError(t, err) + require.Equal(t, c.value, string(newResult)) + } + }) + } +} diff --git a/secrets/encoding/interface.go b/secrets/encoding/interface.go new file mode 100644 index 0000000..f11e9ef --- /dev/null +++ b/secrets/encoding/interface.go @@ -0,0 +1,18 @@ +package encoding + +// Encoder encodes a byte slice and returns a string with the encoding type prefixed +type Encoder interface { + EncodeSecretValue([]byte) (string, error) +} + +// Decoder takes a string and decodes it, returning a byte slice or an error +type Decoder interface { + DecodeSecretValue(string) ([]byte, error) +} + +// EncodeDecoder groups Encoder and Decoder to form a type that can both encode and decode +// secret values. +type EncodeDecoder interface { + Encoder + Decoder +} From e486e836beaf6e3547ca87469a2e12d1b5c8eced Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 11:18:38 -0700 Subject: [PATCH 2/7] Addresses feedback from code review --- secrets/encoding/encoding.go | 14 ++++++--- secrets/encoding/encoding_test.go | 51 ++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/secrets/encoding/encoding.go b/secrets/encoding/encoding.go index 164fe11..c165edc 100644 --- a/secrets/encoding/encoding.go +++ b/secrets/encoding/encoding.go @@ -22,30 +22,34 @@ const ( // unless they really need to. const DefaultEncodingType = Base64EncodingType +// encodingTypeMap is an internal map used to get the encodingType type from a string var encodingTypeMap = map[string]encodingType{ "base64": Base64EncodingType, - "": NoEncodingType, } // ParseEncodedValue will attempt to split on : and extract an encoding identifer // from the prefix of the string. It then returns the discovered encodingType and the // value without the encodingType prefixed. func ParseEncodedValue(value string) (encodingType, string) { - parts := strings.Split(value, ":") + parts := strings.SplitN(value, ":", 2) + + if len(parts) < 2 { + return NoEncodingType, value + } t, ok := encodingTypeMap[parts[0]] if !ok { return NoEncodingType, value } - return t, strings.Join(parts[1:], ":") + return t, parts[1] } // Encoders maps encoding algorithms to their respective EncodeDecoder types. // Example: // -// ed := encoding.Encoders[Base64EncodingType]() -// encodedValue, err := ed.EncodeSecretValue("my super secret value") +// ed := encoding.Encoders[Base64EncodingType]() +// encodedValue, err := ed.EncodeSecretValue("my super secret value") var Encoders = map[encodingType]func() EncodeDecoder{ Base64EncodingType: func() EncodeDecoder { return Base64Encoding{} diff --git a/secrets/encoding/encoding_test.go b/secrets/encoding/encoding_test.go index 4ed9c32..f4aea13 100644 --- a/secrets/encoding/encoding_test.go +++ b/secrets/encoding/encoding_test.go @@ -1,6 +1,7 @@ package encoding import ( + "encoding/base64" "fmt" "testing" @@ -9,10 +10,11 @@ import ( func TestEncoding(t *testing.T) { var cases = []struct { - description string - value string - encodingType encodingType - expected string + description string + value string + encodingType encodingType + expected string + customResultTest func(t *testing.T, encoded string, decoded []byte) }{ { description: "base64 encoding succeeds", @@ -38,6 +40,35 @@ func TestEncoding(t *testing.T) { encodingType: NoEncodingType, expected: "super: secret token:12:49:wheel", }, + { + description: "begins with :", + value: ":fun time at the park", + encodingType: NoEncodingType, + expected: ":fun time at the park", + }, + { + description: "user encoded base64", + value: "c3VwZXIgc2VjcmV0IHRva2Vu", + encodingType: NoEncodingType, + expected: "c3VwZXIgc2VjcmV0IHRva2Vu", + }, + { + description: "user encoded base64 wrapped with our base64 encoder", + // "super secret token" encoded as base64 + value: "c3VwZXIgc2VjcmV0IHRva2Vu", + encodingType: Base64EncodingType, + expected: "base64:YzNWd1pYSWdjMlZqY21WMElIUnZhMlZ1", + customResultTest: func(t *testing.T, encoded string, decoded []byte) { + t.Run("custom result test", func(t *testing.T) { + // tests that a user can encode their own values, have our system wrap it in our own + // encoding, then when they try to unwrap their encoding, they get the expected value. + result, err := base64.StdEncoding.DecodeString(string(decoded)) + require.NoError(t, err) + + require.Equal(t, "super secret token", string(result)) + }) + }, + }, } for _, c := range cases { @@ -53,10 +84,14 @@ func TestEncoding(t *testing.T) { newED := Encoders[typ]() - { - newResult, err := newED.DecodeSecretValue(value) - require.NoError(t, err) - require.Equal(t, c.value, string(newResult)) + var newResult []byte + + newResult, err = newED.DecodeSecretValue(value) + require.NoError(t, err) + require.Equal(t, c.value, string(newResult)) + + if c.customResultTest != nil { + c.customResultTest(t, result, newResult) } }) } From e02a9e5e5ff4f9d55462be5e5e7ea1922e4f36d4 Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 11:21:44 -0700 Subject: [PATCH 3/7] Moves t.Run call for custom result tests to the cases loop --- secrets/encoding/encoding_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/secrets/encoding/encoding_test.go b/secrets/encoding/encoding_test.go index f4aea13..be5bc75 100644 --- a/secrets/encoding/encoding_test.go +++ b/secrets/encoding/encoding_test.go @@ -59,14 +59,12 @@ func TestEncoding(t *testing.T) { encodingType: Base64EncodingType, expected: "base64:YzNWd1pYSWdjMlZqY21WMElIUnZhMlZ1", customResultTest: func(t *testing.T, encoded string, decoded []byte) { - t.Run("custom result test", func(t *testing.T) { - // tests that a user can encode their own values, have our system wrap it in our own - // encoding, then when they try to unwrap their encoding, they get the expected value. - result, err := base64.StdEncoding.DecodeString(string(decoded)) - require.NoError(t, err) + // tests that a user can encode their own values, have our system wrap it in our own + // encoding, then when they try to unwrap their encoding, they get the expected value. + result, err := base64.StdEncoding.DecodeString(string(decoded)) + require.NoError(t, err) - require.Equal(t, "super secret token", string(result)) - }) + require.Equal(t, "super secret token", string(result)) }, }, } @@ -91,7 +89,9 @@ func TestEncoding(t *testing.T) { require.Equal(t, c.value, string(newResult)) if c.customResultTest != nil { - c.customResultTest(t, result, newResult) + t.Run("custom result test", func(t *testing.T) { + c.customResultTest(t, result, newResult) + }) } }) } From a24084a6221d5bedf05cdfb89eeaaeab54963b34 Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 11:50:50 -0700 Subject: [PATCH 4/7] moves secret/encoding package to encoding/transfer In order to conform more to https://tools.ietf.org/html/rfc2045 I have renamed the package to encoding/transfer and I have renamed the interface methods to EncodeForTransfer and DecodeFromTransfer. --- encoding/transfer/doc.go | 23 ++++++++++++++++++ .../transfer}/encoding.go | 24 +++++++++---------- .../transfer}/encoding_test.go | 6 ++--- .../transfer}/interface.go | 9 ++++--- secrets/encoding/doc.go | 23 ------------------ 5 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 encoding/transfer/doc.go rename {secrets/encoding => encoding/transfer}/encoding.go (72%) rename {secrets/encoding => encoding/transfer}/encoding_test.go (95%) rename {secrets/encoding => encoding/transfer}/interface.go (69%) delete mode 100644 secrets/encoding/doc.go diff --git a/encoding/transfer/doc.go b/encoding/transfer/doc.go new file mode 100644 index 0000000..5acde08 --- /dev/null +++ b/encoding/transfer/doc.go @@ -0,0 +1,23 @@ +/* +Package transfer provides an interface for encoding and decoding values +for storage. The utility in this package is transparent to the user +and it is used to maintain byte integrity on values used in workflows. + +Basic use when encoding a value: + encoder := transfer.Encoders[transfer.DefaultEncodingType]() + + result, err := encoder.EncodeForTransfer([]byte("super secret token")) + if err != nil { + // handle error + } + +Basic use when decoding a value: + encodingType, value := transfer.ParseEncodedValue("base64:c3VwZXIgc2VjcmV0IHRva2Vu") + encoder := transfer.Encoders[encoderType]() + + result, err := encoder.DecodeFromTransfer(value) + if err != nil { + // handle error + } +*/ +package transfer diff --git a/secrets/encoding/encoding.go b/encoding/transfer/encoding.go similarity index 72% rename from secrets/encoding/encoding.go rename to encoding/transfer/encoding.go index c165edc..cfc595e 100644 --- a/secrets/encoding/encoding.go +++ b/encoding/transfer/encoding.go @@ -1,4 +1,4 @@ -package encoding +package transfer import ( "encoding/base64" @@ -48,8 +48,8 @@ func ParseEncodedValue(value string) (encodingType, string) { // Encoders maps encoding algorithms to their respective EncodeDecoder types. // Example: // -// ed := encoding.Encoders[Base64EncodingType]() -// encodedValue, err := ed.EncodeSecretValue("my super secret value") +// ed := transfer.Encoders[Base64EncodingType]() +// encodedValue, err := ed.EncodeForTransfer("my super secret value") var Encoders = map[encodingType]func() EncodeDecoder{ Base64EncodingType: func() EncodeDecoder { return Base64Encoding{} @@ -59,21 +59,21 @@ var Encoders = map[encodingType]func() EncodeDecoder{ }, } -// Base64Encoding handles the encoding and decoding of secret values using base64. +// Base64Encoding handles the encoding and decoding of values using base64. // All encoded values will be prefixed with "base64:" type Base64Encoding struct{} -// EncodeSecretValue takes a byte slice and returns it encoded as a base64 string. +// EncodeForTransfer takes a byte slice and returns it encoded as a base64 string. // No error is ever returned. -func (e Base64Encoding) EncodeSecretValue(value []byte) (string, error) { +func (e Base64Encoding) EncodeForTransfer(value []byte) (string, error) { s := base64.StdEncoding.EncodeToString(value) return fmt.Sprintf("%s:%s", Base64EncodingType, s), nil } -// DecodeSecretValue takes a string and attempts to decode using a base64 decoder. +// DecodeFromTransfer takes a string and attempts to decode using a base64 decoder. // If an error is returned, it will originate from the Go encoding/base64 package. -func (e Base64Encoding) DecodeSecretValue(value string) ([]byte, error) { +func (e Base64Encoding) DecodeFromTransfer(value string) ([]byte, error) { return base64.StdEncoding.DecodeString(value) } @@ -81,14 +81,14 @@ func (e Base64Encoding) DecodeSecretValue(value string) ([]byte, error) { // is no encoding type algorithm prefix on the value. type NoEncoding struct{} -// EncodeSecretValue takes a byte slice and casts it to a string. No error is ever +// EncodeForTransfer takes a byte slice and casts it to a string. No error is ever // returned. -func (e NoEncoding) EncodeSecretValue(value []byte) (string, error) { +func (e NoEncoding) EncodeForTransfer(value []byte) (string, error) { return string(value), nil } -// DecodeSecretValue takes a string and casts it to a byte slice. No error is ever +// DecodeFromTransfer takes a string and casts it to a byte slice. No error is ever // returned. -func (e NoEncoding) DecodeSecretValue(value string) ([]byte, error) { +func (e NoEncoding) DecodeFromTransfer(value string) ([]byte, error) { return []byte(value), nil } diff --git a/secrets/encoding/encoding_test.go b/encoding/transfer/encoding_test.go similarity index 95% rename from secrets/encoding/encoding_test.go rename to encoding/transfer/encoding_test.go index be5bc75..58101b1 100644 --- a/secrets/encoding/encoding_test.go +++ b/encoding/transfer/encoding_test.go @@ -1,4 +1,4 @@ -package encoding +package transfer import ( "encoding/base64" @@ -73,7 +73,7 @@ func TestEncoding(t *testing.T) { t.Run(c.description, func(t *testing.T) { ed := Encoders[c.encodingType]() - result, err := ed.EncodeSecretValue([]byte(c.value)) + result, err := ed.EncodeForTransfer([]byte(c.value)) require.NoError(t, err) require.Equal(t, c.expected, result, fmt.Sprintf("result was malformed: %s", result)) @@ -84,7 +84,7 @@ func TestEncoding(t *testing.T) { var newResult []byte - newResult, err = newED.DecodeSecretValue(value) + newResult, err = newED.DecodeFromTransfer(value) require.NoError(t, err) require.Equal(t, c.value, string(newResult)) diff --git a/secrets/encoding/interface.go b/encoding/transfer/interface.go similarity index 69% rename from secrets/encoding/interface.go rename to encoding/transfer/interface.go index f11e9ef..8bebdbc 100644 --- a/secrets/encoding/interface.go +++ b/encoding/transfer/interface.go @@ -1,17 +1,16 @@ -package encoding +package transfer // Encoder encodes a byte slice and returns a string with the encoding type prefixed type Encoder interface { - EncodeSecretValue([]byte) (string, error) + EncodeForTransfer([]byte) (string, error) } // Decoder takes a string and decodes it, returning a byte slice or an error type Decoder interface { - DecodeSecretValue(string) ([]byte, error) + DecodeFromTransfer(string) ([]byte, error) } -// EncodeDecoder groups Encoder and Decoder to form a type that can both encode and decode -// secret values. +// EncodeDecoder groups Encoder and Decoder to form a type that can both encode and decode values. type EncodeDecoder interface { Encoder Decoder diff --git a/secrets/encoding/doc.go b/secrets/encoding/doc.go deleted file mode 100644 index d7939af..0000000 --- a/secrets/encoding/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Package encoding provides an interface for encoding and decoding secret -values for storage. The utility in this package is transparent to the user -and it is used to maintain byte integrity on secret values used in workflows. - -Basic use when encoding a value: - encoder := encoding.Encoders[encoding.DefaultEncodingType]() - - result, err := encoder.EncodeSecretValue([]byte("super secret token")) - if err != nil { - // handle error - } - -Basic use when decoding a value: - encodingType, value := encoding.ParseEncodedValue("base64:c3VwZXIgc2VjcmV0IHRva2Vu") - encoder := encoding.Encoders[encoderType]() - - result, err := encoder.DecodeSecretValue(value) - if err != nil { - // handle error - } -*/ -package encoding From 7423998bfa85bac7469404eef530c9bf56cf0b03 Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 13:14:05 -0700 Subject: [PATCH 5/7] Adds top level helper functions: EncodeForTransfer/DecodeFromTransfer --- encoding/transfer/encoding.go | 16 ++++++++++ encoding/transfer/encoding_test.go | 49 +++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/encoding/transfer/encoding.go b/encoding/transfer/encoding.go index cfc595e..6aa231b 100644 --- a/encoding/transfer/encoding.go +++ b/encoding/transfer/encoding.go @@ -92,3 +92,19 @@ func (e NoEncoding) EncodeForTransfer(value []byte) (string, error) { func (e NoEncoding) DecodeFromTransfer(value string) ([]byte, error) { return []byte(value), nil } + +// EncodeForTransfer uses the DefaultEncodingType to encode value. +func EncodeForTransfer(value []byte) (string, error) { + encoder := Encoders[DefaultEncodingType]() + + return encoder.EncodeForTransfer(value) +} + +// DecodeFromTransfer uses ParseEncodedValue to find the right encoder then +// decodes value with it. +func DecodeFromTransfer(value string) ([]byte, error) { + t, val := ParseEncodedValue(value) + encoder := Encoders[t]() + + return encoder.DecodeFromTransfer(val) +} diff --git a/encoding/transfer/encoding_test.go b/encoding/transfer/encoding_test.go index 58101b1..b89e2c4 100644 --- a/encoding/transfer/encoding_test.go +++ b/encoding/transfer/encoding_test.go @@ -73,26 +73,61 @@ func TestEncoding(t *testing.T) { t.Run(c.description, func(t *testing.T) { ed := Encoders[c.encodingType]() - result, err := ed.EncodeForTransfer([]byte(c.value)) + encoded, err := ed.EncodeForTransfer([]byte(c.value)) require.NoError(t, err) - require.Equal(t, c.expected, result, fmt.Sprintf("result was malformed: %s", result)) + require.Equal(t, c.expected, encoded, fmt.Sprintf("encoding result was malformed: %s", encoded)) - typ, value := ParseEncodedValue(result) + typ, value := ParseEncodedValue(encoded) require.Equal(t, c.encodingType, typ) newED := Encoders[typ]() - var newResult []byte + var decoded []byte - newResult, err = newED.DecodeFromTransfer(value) + decoded, err = newED.DecodeFromTransfer(value) require.NoError(t, err) - require.Equal(t, c.value, string(newResult)) + require.Equal(t, c.value, string(decoded)) if c.customResultTest != nil { t.Run("custom result test", func(t *testing.T) { - c.customResultTest(t, result, newResult) + c.customResultTest(t, encoded, decoded) }) } }) } } + +func TestHelperFuncs(t *testing.T) { + var cases = []struct { + description string + value string + expected string + }{ + { + description: "base64 encoding succeeds", + value: "super secret token", + expected: "base64:c3VwZXIgc2VjcmV0IHRva2Vu", + }, + { + description: "user encoded base64 wrapped with our base64 encoder", + // "super secret token" encoded as base64 + value: "c3VwZXIgc2VjcmV0IHRva2Vu", + expected: "base64:YzNWd1pYSWdjMlZqY21WMElIUnZhMlZ1", + }, + } + + for _, c := range cases { + t.Run(c.description, func(t *testing.T) { + encoded, err := EncodeForTransfer([]byte(c.value)) + require.NoError(t, err) + + require.Equal(t, c.expected, encoded) + + var decoded []byte + + decoded, err = DecodeFromTransfer(encoded) + require.NoError(t, err) + require.Equal(t, c.value, string(decoded)) + }) + } +} From 13a08e04b108547ed5fccd5752997b1f484551f0 Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 13:27:41 -0700 Subject: [PATCH 6/7] Updates readme to reflect package name change for encoding --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c36b0a8..c067731 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ The functionality in this package should work on Linux, MacOS and the BSDs. - add a mechanism for root interactions - add Windows support -## secrets/encoding +## encoding/transfer -This package provides an interface to encode and decode secret values that will get stored -in the secret store. This is required to ensure that values are consistently stored safely, -but also doesn't enforce an encoding that users must use when storing secrets. The default -algorithm is base64 and all encoded strings generated will be prefixed with "base64:". If -there is no encoding prefix, there is `NoEncodingType` that will just return the original -value unencoded. +This package provides an interface to encode and decode values that will get stored +in a data store. This is required to ensure that values are consistently stored safely, +but also doesn't enforce an encoding that users must use when storing secrets or outputs. +The default algorithm is base64 and all encoded strings generated will be prefixed with "base64:". +If there is no encoding prefix, there is `NoEncodingType` that will just return the original +value unencoded/decoded. -Help can be found by running `go doc -all github.com/puppetlabs/horsehead/secrets/encoding`. +Help can be found by running `go doc -all github.com/puppetlabs/horsehead/encoding/transfer`. From 66198273ffd1cd9cc9ddafbad9e3d6cb352789bc Mon Sep 17 00:00:00 2001 From: Kyle Terry Date: Wed, 14 Aug 2019 13:35:08 -0700 Subject: [PATCH 7/7] New: Add encoding/transfer package