Skip to content

Commit

Permalink
Merge pull request #1 from pfcoperez/feature/shared_settings
Browse files Browse the repository at this point in the history
Wrap serdes parameters around the `Settings` interface
  • Loading branch information
pfcoperez authored May 8, 2023
2 parents 218d1e0 + 56af00d commit b619b09
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 18 deletions.
6 changes: 4 additions & 2 deletions demo/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ func main() {
fmt.Println(string(marshalledAllSecrets))

// But we can set some fields open for JSON serialization
snowCrash.Characters[1].Name.CleartextJSON = true
snowCrash.Characters[1].Age.CleartextJSON = true
changedSettings := NewImmutableSettings(true)

snowCrash.Characters[1].Name.Settings = changedSettings
snowCrash.Characters[1].Age.Settings = changedSettings

marshalledSomeSecrets, _ := json.Marshal(snowCrash)
fmt.Println(string(marshalledSomeSecrets))
Expand Down
67 changes: 64 additions & 3 deletions gosecretfields.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,70 @@ type Secret[T any] struct {
// This is the value that replaces the secret value upon redaction.
redactedValue T

// Flag toggling redaction on and off for different kinds of serialization
// so far, only JSON serdes is configurable.
// By assigning references to the same value implementing the `Settings` interface,
// we are able to control a group of `Secret` instances serdes behaviour at the same time.
Settings Settings
}

// Common interface for per-secret instance settings.
type Settings interface {
// Flag controling wether this secret should be JSON serialized with
// its secret or redacted value.
CleartextJSON bool
CleartextJSON() bool
}

// Implementation of the `Settings` interface that close down
// changes after initialization.
type ImmutableSettings struct {
enabledClearTextJSON bool
}

func NewImmutableSettings(enabledClearTextJSON bool) ImmutableSettings {
return ImmutableSettings{enabledClearTextJSON}
}

func (is ImmutableSettings) CleartextJSON() bool {
return is.enabledClearTextJSON
}

func (is *ImmutableSettings) CopyAsMutableSettings() MutableSettings {
return MutableSettings{
EnabledClearTextJSON: is.CleartextJSON(),
}
}

// Implementation of the `Settings` interface allowing for in place
// mutation.
type MutableSettings struct {
EnabledClearTextJSON bool
}

func (ms *MutableSettings) CleartextJSON() bool {
return ms.EnabledClearTextJSON
}

func (ms *MutableSettings) Copy() MutableSettings {
return MutableSettings{
EnabledClearTextJSON: ms.CleartextJSON(),
}
}

func (ms *MutableSettings) CopyAsImmutable() ImmutableSettings {
return ImmutableSettings{
enabledClearTextJSON: ms.CleartextJSON(),
}
}

// Shared static values

var defaultSettings = ImmutableSettings{
enabledClearTextJSON: false,
}

func DefaultSettings() Settings {
return &defaultSettings
}

// Factories
Expand All @@ -39,7 +100,7 @@ func AsSecret[T any](value T, redactedValue ...T) Secret[T] {
return Secret[T]{
SecretValue: value,
redactedValue: redacted,
CleartextJSON: false,
Settings: DefaultSettings(),
}
}

Expand All @@ -50,7 +111,7 @@ func (s Secret[T]) MarshalJSON() ([]byte, error) {
// Secret fields will result on the redactedValue or the actual secret value JSON representation
// but the container will never show in the JSON structure.
safeValue := s.redactedValue
if s.CleartextJSON {
if s.Settings.CleartextJSON() {
safeValue = s.SecretValue
}
return json.Marshal(safeValue)
Expand Down
29 changes: 16 additions & 13 deletions gosecretfields_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ func TestAsSecret(t *testing.T) {
value: "Hiro Protagonist",
redactedValue: []string{},
},
want: Secret[string]{SecretValue: "Hiro Protagonist", redactedValue: ""},
want: Secret[string]{SecretValue: "Hiro Protagonist", redactedValue: "", Settings: DefaultSettings()},
},
{
name: "With explicit redacted value",
args: args{
value: "Hiro Protagonist",
redactedValue: []string{"REDACTED"},
},
want: Secret[string]{SecretValue: "Hiro Protagonist", redactedValue: "REDACTED"},
want: Secret[string]{SecretValue: "Hiro Protagonist", redactedValue: "REDACTED", Settings: DefaultSettings()},
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -75,6 +75,15 @@ func containsSecrets(text string) bool {

func TestMarshallingAndStringer(t *testing.T) {

commonSettings := &MutableSettings{
EnabledClearTextJSON: true,
}

sampleCharacterHiro.Age.Settings = commonSettings
sampleCharacterHiro.Name.Settings = commonSettings
sampleCharacterYT.Age.Settings = commonSettings
sampleCharacterYT.Name.Settings = commonSettings

hiroWithFriend := sampleCharacterHiro
hiroWithFriend.Friend = &sampleCharacterYT

Expand Down Expand Up @@ -107,13 +116,7 @@ func TestMarshallingAndStringer(t *testing.T) {
t.Errorf("Stringer interface implementation must prevent secrets from leaking:\n%s", asString)
}

testCase.value.Name.CleartextJSON = !testCase.redactJson
testCase.value.Age.CleartextJSON = !testCase.redactJson

if maybeFriend := testCase.value.Friend; maybeFriend != nil {
maybeFriend.Name.CleartextJSON = !testCase.redactJson
maybeFriend.Age.CleartextJSON = !testCase.redactJson
}
commonSettings.EnabledClearTextJSON = !testCase.redactJson

var unmarshalled character

Expand All @@ -130,12 +133,12 @@ func TestMarshallingAndStringer(t *testing.T) {
t.Errorf("Unmarshalling should not fail. Error: %s", unmarshallingError)
}

unmarshalled.Name.CleartextJSON = !testCase.redactJson
unmarshalled.Age.CleartextJSON = !testCase.redactJson
unmarshalled.Name.Settings = commonSettings
unmarshalled.Age.Settings = commonSettings

if maybeUnmarshalledFriend := unmarshalled.Friend; maybeUnmarshalledFriend != nil {
maybeUnmarshalledFriend.Name.CleartextJSON = !testCase.redactJson
maybeUnmarshalledFriend.Age.CleartextJSON = !testCase.redactJson
maybeUnmarshalledFriend.Name.Settings = commonSettings
maybeUnmarshalledFriend.Age.Settings = commonSettings
}

if !testCase.redactJson && !reflect.DeepEqual(unmarshalled, testCase.value) {
Expand Down

0 comments on commit b619b09

Please sign in to comment.