From 992b04d180ddba62e64a3a92c906ef305bb57139 Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Mon, 2 Jan 2023 11:39:58 +0100 Subject: [PATCH] Allow alternative syntax for auth0 tenants as environment variable --- config/value/auth0.go | 57 ++++++++++++++++++++++++++++++++------ config/value/auth0_test.go | 43 ++++++++++++++++++++++++++++ config/value/value_test.go | 32 ++++++++++----------- 3 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 config/value/auth0_test.go diff --git a/config/value/auth0.go b/config/value/auth0.go index 8d19b4f1..a912134d 100644 --- a/config/value/auth0.go +++ b/config/value/auth0.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/url" "strings" ) @@ -16,6 +17,28 @@ type Auth0Tenant struct { Users []string `json:"users"` } +func (a *Auth0Tenant) String() string { + u := url.URL{ + Scheme: "auth0", + Host: a.Domain, + } + + if len(a.ClientID) != 0 { + u.User = url.User(a.ClientID) + } + + q := url.Values{} + q.Set("aud", a.Audience) + + for _, user := range a.Users { + q.Add("user", user) + } + + u.RawQuery = q.Encode() + + return u.String() +} + type TenantList struct { p *[]Auth0Tenant separator string @@ -32,18 +55,34 @@ func NewTenantList(p *[]Auth0Tenant, val []Auth0Tenant, separator string) *Tenan return v } +// Set allows to set a tenant list in two formats: +// - a separator separated list of bas64 encoded Auth0Tenant JSON objects +// - a separator separated list of Auth0Tenant in URL representation: auth0://[clientid]@[domain]?aud=[audience]&user=...&user=... func (s *TenantList) Set(val string) error { list := []Auth0Tenant{} for i, elm := range strings.Split(val, s.separator) { - data, err := base64.StdEncoding.DecodeString(elm) - if err != nil { - return fmt.Errorf("invalid base64 encoding of tenant %d: %w", i, err) - } - t := Auth0Tenant{} - if err := json.Unmarshal(data, &t); err != nil { - return fmt.Errorf("invalid JSON in tenant %d: %w", i, err) + + if strings.HasPrefix(elm, "auth0://") { + data, err := url.Parse(elm) + if err != nil { + return fmt.Errorf("invalid url encoding of tenant %d: %w", i, err) + } + + t.Domain = data.Host + t.ClientID = data.User.Username() + t.Audience = data.Query().Get("aud") + t.Users = data.Query()["user"] + } else { + data, err := base64.StdEncoding.DecodeString(elm) + if err != nil { + return fmt.Errorf("invalid base64 encoding of tenant %d: %w", i, err) + } + + if err := json.Unmarshal(data, &t); err != nil { + return fmt.Errorf("invalid JSON in tenant %d: %w", i, err) + } } list = append(list, t) @@ -62,10 +101,10 @@ func (s *TenantList) String() string { list := []string{} for _, t := range *s.p { - list = append(list, fmt.Sprintf("%s (%d users)", t.Domain, len(t.Users))) + list = append(list, t.String()) } - return strings.Join(list, ",") + return strings.Join(list, s.separator) } func (s *TenantList) Validate() error { diff --git a/config/value/auth0_test.go b/config/value/auth0_test.go new file mode 100644 index 00000000..edc4eff8 --- /dev/null +++ b/config/value/auth0_test.go @@ -0,0 +1,43 @@ +package value + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAuth0Value(t *testing.T) { + tenants := []Auth0Tenant{} + + v := NewTenantList(&tenants, nil, " ") + require.Equal(t, "(empty)", v.String()) + + v.Set("auth0://clientid@domain?aud=audience&user=user1&user=user2 auth0://domain2?aud=audience2&user=user3") + require.Equal(t, []Auth0Tenant{ + { + Domain: "domain", + ClientID: "clientid", + Audience: "audience", + Users: []string{"user1", "user2"}, + }, + { + Domain: "domain2", + Audience: "audience2", + Users: []string{"user3"}, + }, + }, tenants) + require.Equal(t, "auth0://clientid@domain?aud=audience&user=user1&user=user2 auth0://domain2?aud=audience2&user=user3", v.String()) + require.NoError(t, v.Validate()) + + v.Set("eyJkb21haW4iOiJkYXRhcmhlaS5ldS5hdXRoMC5jb20iLCJhdWRpZW5jZSI6Imh0dHBzOi8vZGF0YXJoZWkuY29tL2NvcmUiLCJ1c2VycyI6WyJhdXRoMHx4eHgiXX0=") + require.Equal(t, []Auth0Tenant{ + { + Domain: "datarhei.eu.auth0.com", + ClientID: "", + Audience: "https://datarhei.com/core", + Users: []string{"auth0|xxx"}, + }, + }, tenants) + require.Equal(t, "auth0://datarhei.eu.auth0.com?aud=https%3A%2F%2Fdatarhei.com%2Fcore&user=auth0%7Cxxx", v.String()) + require.NoError(t, v.Validate()) +} diff --git a/config/value/value_test.go b/config/value/value_test.go index 49c024e9..aeb707be 100644 --- a/config/value/value_test.go +++ b/config/value/value_test.go @@ -3,7 +3,7 @@ package value import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestIntValue(t *testing.T) { @@ -11,19 +11,19 @@ func TestIntValue(t *testing.T) { ivar := NewInt(&i, 11) - assert.Equal(t, "11", ivar.String()) - assert.Equal(t, nil, ivar.Validate()) - assert.Equal(t, false, ivar.IsEmpty()) + require.Equal(t, "11", ivar.String()) + require.Equal(t, nil, ivar.Validate()) + require.Equal(t, false, ivar.IsEmpty()) i = 42 - assert.Equal(t, "42", ivar.String()) - assert.Equal(t, nil, ivar.Validate()) - assert.Equal(t, false, ivar.IsEmpty()) + require.Equal(t, "42", ivar.String()) + require.Equal(t, nil, ivar.Validate()) + require.Equal(t, false, ivar.IsEmpty()) ivar.Set("77") - assert.Equal(t, int(77), i) + require.Equal(t, int(77), i) } type testdata struct { @@ -37,22 +37,22 @@ func TestCopyStruct(t *testing.T) { NewInt(&data1.value1, 1) NewInt(&data1.value2, 2) - assert.Equal(t, int(1), data1.value1) - assert.Equal(t, int(2), data1.value2) + require.Equal(t, int(1), data1.value1) + require.Equal(t, int(2), data1.value2) data2 := testdata{} val21 := NewInt(&data2.value1, 3) val22 := NewInt(&data2.value2, 4) - assert.Equal(t, int(3), data2.value1) - assert.Equal(t, int(4), data2.value2) + require.Equal(t, int(3), data2.value1) + require.Equal(t, int(4), data2.value2) data2 = data1 - assert.Equal(t, int(1), data2.value1) - assert.Equal(t, int(2), data2.value2) + require.Equal(t, int(1), data2.value1) + require.Equal(t, int(2), data2.value2) - assert.Equal(t, "1", val21.String()) - assert.Equal(t, "2", val22.String()) + require.Equal(t, "1", val21.String()) + require.Equal(t, "2", val22.String()) }