config: fix unmarshalling of secret URLs (#1663)

* config: fix unmarshalling of secret URLs

* Add comment describing why we need the special case

Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
Simon Pasquier 2018-12-13 11:09:03 +01:00 committed by stuart nelson
parent f1d33fbcc2
commit 34f78c9146
2 changed files with 46 additions and 4 deletions

View File

@ -29,13 +29,25 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
const secretToken = "<secret>"
var secretTokenJSON string
func init() {
b, err := json.Marshal(secretToken)
if err != nil {
panic(err)
}
secretTokenJSON = string(b)
}
// Secret is a string that must not be revealed on marshaling. // Secret is a string that must not be revealed on marshaling.
type Secret string type Secret string
// MarshalYAML implements the yaml.Marshaler interface. // MarshalYAML implements the yaml.Marshaler interface.
func (s Secret) MarshalYAML() (interface{}, error) { func (s Secret) MarshalYAML() (interface{}, error) {
if s != "" { if s != "" {
return "<secret>", nil return secretToken, nil
} }
return nil, nil return nil, nil
} }
@ -48,7 +60,7 @@ func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error {
// MarshalJSON implements the json.Marshaler interface. // MarshalJSON implements the json.Marshaler interface.
func (s Secret) MarshalJSON() ([]byte, error) { func (s Secret) MarshalJSON() ([]byte, error) {
return json.Marshal("<secret>") return json.Marshal(secretToken)
} }
// URL is a custom type that represents an HTTP or HTTPS URL and allows validation at configuration load time. // URL is a custom type that represents an HTTP or HTTPS URL and allows validation at configuration load time.
@ -112,23 +124,41 @@ type SecretURL URL
// MarshalYAML implements the yaml.Marshaler interface for SecretURL. // MarshalYAML implements the yaml.Marshaler interface for SecretURL.
func (s SecretURL) MarshalYAML() (interface{}, error) { func (s SecretURL) MarshalYAML() (interface{}, error) {
if s.URL != nil { if s.URL != nil {
return "<secret>", nil return secretToken, nil
} }
return nil, nil return nil, nil
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface for SecretURL. // UnmarshalYAML implements the yaml.Unmarshaler interface for SecretURL.
func (s *SecretURL) UnmarshalYAML(unmarshal func(interface{}) error) error { func (s *SecretURL) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err != nil {
return err
}
// In order to deserialize a previously serialized configuration (eg from
// the Alertmanager API with amtool), `<secret>` needs to be treated
// specially, as it isn't a valid URL.
if str == secretToken {
s.URL = &url.URL{}
return nil
}
return unmarshal((*URL)(s)) return unmarshal((*URL)(s))
} }
// MarshalJSON implements the json.Marshaler interface for SecretURL. // MarshalJSON implements the json.Marshaler interface for SecretURL.
func (s SecretURL) MarshalJSON() ([]byte, error) { func (s SecretURL) MarshalJSON() ([]byte, error) {
return json.Marshal("<secret>") return json.Marshal(secretToken)
} }
// UnmarshalJSON implements the json.Marshaler interface for SecretURL. // UnmarshalJSON implements the json.Marshaler interface for SecretURL.
func (s *SecretURL) UnmarshalJSON(data []byte) error { func (s *SecretURL) UnmarshalJSON(data []byte) error {
// In order to deserialize a previously serialized configuration (eg from
// the Alertmanager API with amtool), `<secret>` needs to be treated
// specially, as it isn't a valid URL.
if string(data) == secretToken || string(data) == secretTokenJSON {
s.URL = &url.URL{}
return nil
}
return json.Unmarshal(data, (*URL)(s)) return json.Unmarshal(data, (*URL)(s))
} }

View File

@ -346,12 +346,24 @@ func TestMarshalSecretURL(t *testing.T) {
// u003c -> "<" // u003c -> "<"
// u003e -> ">" // u003e -> ">"
require.Equal(t, "\"\\u003csecret\\u003e\"", string(c), "SecretURL not properly elided in JSON.") require.Equal(t, "\"\\u003csecret\\u003e\"", string(c), "SecretURL not properly elided in JSON.")
// Check that the marshaled data can be unmarshaled again.
out := &SecretURL{}
err = json.Unmarshal(c, out)
if err != nil {
t.Fatal(err)
}
c, err = yaml.Marshal(u) c, err = yaml.Marshal(u)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
require.Equal(t, "<secret>\n", string(c), "SecretURL not properly elided in YAML.") require.Equal(t, "<secret>\n", string(c), "SecretURL not properly elided in YAML.")
// Check that the marshaled data can be unmarshaled again.
out = &SecretURL{}
err = yaml.Unmarshal(c, &out)
if err != nil {
t.Fatal(err)
}
} }
func TestUnmarshalSecretURL(t *testing.T) { func TestUnmarshalSecretURL(t *testing.T) {