Skip to content

Commit

Permalink
feat: HTTP Hook - Add custom envconfig decoding for HTTP Hook Secrets (
Browse files Browse the repository at this point in the history
…#1467)

## What kind of change does this PR introduce?

We represent HTTP Hooks as a `|` separated list, similar to what we do
with required password characters. Asymmetric keys are separated by `:`
like: `v1a,whpk_mypublickey|whsk_mysecretkey:v1,whsec_mysymettrickey`

We opt for `:` and `|` as they are not part of the base64 alphabet which
we use to store secrets.

Co-authored-by: joel <[email protected]>
  • Loading branch information
J0 and joel authored Mar 13, 2024
1 parent 35aeaf1 commit 5b24c4e
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 8 deletions.
24 changes: 19 additions & 5 deletions internal/conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var postgresNamesRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]{0,62}$`)
// So this 4 * Math.ceil(24/3) = 32 and 4 * Math.ceil(64/3) = 88 for symmetric secrets
// Since Ed25519 key is 32 bytes so we have 4 * Math.ceil(32/3) = 44
var symmetricSecretFormat = regexp.MustCompile(`^v1,whsec_[A-Za-z0-9+/=]{32,88}`)
var asymmetricSecretFormat = regexp.MustCompile(`^v1a,whpk_[A-Za-z0-9+/=]{44,};whsk_[A-Za-z0-9+/=]{44,}$`)
var asymmetricSecretFormat = regexp.MustCompile(`^v1a,whpk_[A-Za-z0-9+/=]{44,}:whsk_[A-Za-z0-9+/=]{44,}$`)

// Time is used to represent timestamps in the configuration, as envconfig has
// trouble parsing empty strings, due to time.Time.UnmarshalText().
Expand Down Expand Up @@ -452,11 +452,25 @@ type HookConfiguration struct {
CustomSMSProvider ExtensibilityPointConfiguration `json:"custom_sms_provider" split_words:"true"`
}

type HTTPHookSecrets []string

func (h *HTTPHookSecrets) Decode(value string) error {
parts := strings.Split(value, "|")
for _, part := range parts {
if part != "" {
*h = append(*h, part)
}
}

return nil
}

type ExtensibilityPointConfiguration struct {
URI string `json:"uri"`
Enabled bool `json:"enabled"`
HookName string `json:"hook_name"`
HTTPHookSecrets []string `json:"secrets"`
URI string `json:"uri"`
Enabled bool `json:"enabled"`
HookName string `json:"hook_name"`
// We use | as a separator for keys and : as a separator for keys within a keypair. For instance: v1,whsec_test|v1a,whpk_myother:v1a,whsk_testkey|v1,whsec_secret3
HTTPHookSecrets []string `json:"secrets" envconfig:"secrets"`
}

func (h *HookConfiguration) Validate() error {
Expand Down
57 changes: 54 additions & 3 deletions internal/conf/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,57 @@ func TestPasswordRequiredCharactersDecode(t *testing.T) {
}
}

func TestHTTPHookSecretsDecode(t *testing.T) {
examples := []struct {
Value string
Result []string
}{
{
Value: "v1,whsec_secret1|v1a,whpk_secrets:whsk_secret2|v1,whsec_secret3",
Result: []string{"v1,whsec_secret1", "v1a,whpk_secrets:whsk_secret2", "v1,whsec_secret3"},
},
{
Value: "v1,whsec_singlesecret",
Result: []string{"v1,whsec_singlesecret"},
},
{
Value: " ",
Result: []string{" "},
},
{
Value: "",
Result: nil,
},
{
Value: "|a|b|c",
Result: []string{
"a",
"b",
"c",
},
},
{
Value: "||||",
Result: nil,
},
{
Value: "::",
Result: []string{"::"},
},
{
Value: "secret1::secret3",
Result: []string{"secret1::secret3"},
},
}

for i, example := range examples {
var into HTTPHookSecrets

require.NoError(t, into.Decode(example.Value), "Example %d failed with error", i)
require.Equal(t, []string(into), example.Result, "Example %d got unexpected result", i)
}
}

func TestValidateExtensibilityPointURI(t *testing.T) {
cases := []struct {
desc string
Expand Down Expand Up @@ -138,11 +189,11 @@ func TestValidateExtensibilityPointSecrets(t *testing.T) {
}{
// Positive test cases
{desc: "Valid Symmetric Secret", secret: []string{"v1,whsec_NDYzODhlNTY0ZGI1OWZjYTU2NjMwN2FhYzM3YzBkMWQ0NzVjNWRkNTJmZDU0MGNhYTAzMjVjNjQzMzE3Mjk2Zg====="}, expectError: false},
{desc: "Valid Asymmetric Secret", secret: []string{"v1a,whpk_NDYzODhlNTY0ZGI1OWZjYTU2NjMwN2FhYzM3YzBkMWQ0NzVjNWRkNTJmZDU0MGNhYTAzMjVjNjQzMzE3Mjk2Zg==;whsk_abc889a6b1160015025064f108a48d6aba1c7c95fa8e304b4d225e8ae0121511"}, expectError: false},
{desc: "Valid Mix of Symmetric and asymmetric Secret", secret: []string{"v1,whsec_2b49264c90fd15db3bb0e05f4e1547b9c183eb06d585be8a", "v1a,whpk_46388e564db59fca566307aac37c0d1d475c5dd52fd540caa0325c643317296f;whsk_YWJjODg5YTZiMTE2MDAxNTAyNTA2NGYxMDhhNDhkNmFiYTFjN2M5NWZhOGUzMDRiNGQyMjVlOGFlMDEyMTUxMSI="}, expectError: false},
{desc: "Valid Asymmetric Secret", secret: []string{"v1a,whpk_NDYzODhlNTY0ZGI1OWZjYTU2NjMwN2FhYzM3YzBkMWQ0NzVjNWRkNTJmZDU0MGNhYTAzMjVjNjQzMzE3Mjk2Zg==:whsk_abc889a6b1160015025064f108a48d6aba1c7c95fa8e304b4d225e8ae0121511"}, expectError: false},
{desc: "Valid Mix of Symmetric and asymmetric Secret", secret: []string{"v1,whsec_2b49264c90fd15db3bb0e05f4e1547b9c183eb06d585be8a", "v1a,whpk_46388e564db59fca566307aac37c0d1d475c5dd52fd540caa0325c643317296f:whsk_YWJjODg5YTZiMTE2MDAxNTAyNTA2NGYxMDhhNDhkNmFiYTFjN2M5NWZhOGUzMDRiNGQyMjVlOGFlMDEyMTUxMSI="}, expectError: false},

// Negative test cases
{desc: "Invalid Asymmetric Secret", secret: []string{"v1a,john;jill", "jill"}, expectError: true},
{desc: "Invalid Asymmetric Secret", secret: []string{"v1a,john:jill", "jill"}, expectError: true},
{desc: "Invalid Symmetric Secret", secret: []string{"tommy"}, expectError: true},
}
for _, tc := range cases {
Expand Down

0 comments on commit 5b24c4e

Please sign in to comment.