From 14bf772923542546b25e1e9c545093d2f0346ffc Mon Sep 17 00:00:00 2001 From: Zixuan Liu Date: Wed, 2 Nov 2022 22:58:42 +0800 Subject: [PATCH 1/2] Add data url format to read the key file Signed-off-by: Zixuan Liu --- oauth2/client_credentials_provider.go | 10 +++ oauth2/client_credentials_provider_test.go | 75 ++++++++++++++++++++++ oauth2/data_url.go | 70 ++++++++++++++++++++ oauth2/data_url_test.go | 58 +++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 oauth2/client_credentials_provider_test.go create mode 100644 oauth2/data_url.go create mode 100644 oauth2/data_url_test.go diff --git a/oauth2/client_credentials_provider.go b/oauth2/client_credentials_provider.go index c112225e5b..402de2c862 100644 --- a/oauth2/client_credentials_provider.go +++ b/oauth2/client_credentials_provider.go @@ -19,6 +19,7 @@ package oauth2 import ( "encoding/json" + "fmt" "io/ioutil" "strings" ) @@ -57,6 +58,15 @@ func (k *KeyFileProvider) GetClientCredentials() (*KeyFile, error) { keyFile, err = ioutil.ReadFile(filename) case strings.HasPrefix(k.KeyFile, DATA): keyFile = []byte(strings.TrimPrefix(k.KeyFile, DATA)) + case strings.HasPrefix(k.KeyFile, "data:"): + url, err := newDataURL(k.KeyFile) + if err != nil { + return nil, err + } + if url.Mimetype != "application/json" { + return nil, fmt.Errorf("unsupported mimetype: %s", url.Mimetype) + } + keyFile = url.Data default: keyFile, err = ioutil.ReadFile(k.KeyFile) } diff --git a/oauth2/client_credentials_provider_test.go b/oauth2/client_credentials_provider_test.go new file mode 100644 index 0000000000..8296098c14 --- /dev/null +++ b/oauth2/client_credentials_provider_test.go @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package oauth2 + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewClientCredentialsProviderFromKeyFile(t *testing.T) { + oauthType := "TYPE" + clientID := "CLIENT_ID" + ClientSecret := "CLIENT_SECRET" + ClientEmail := "CLIENT_EMAIL" + IssuerURL := "ISSUER_URL" + keyFile := &KeyFile{ + Type: oauthType, + ClientID: clientID, + ClientSecret: ClientSecret, + ClientEmail: ClientEmail, + IssuerURL: IssuerURL, + } + + b, err := json.Marshal(keyFile) + require.NoError(t, err) + tmpFile, err := ioutil.TempFile("", "key-file") + require.NoError(t, err) + defer func(name string) { + _ = os.Remove(name) + }(tmpFile.Name()) + _, err = tmpFile.Write(b) + require.NoError(t, err) + + assertCredentials(t, fmt.Sprintf("file://%s", tmpFile.Name()), keyFile) + assertCredentials(t, fmt.Sprintf("data://%s", string(b)), keyFile) + assertCredentials(t, fmt.Sprintf("data:application/json,%s", string(b)), keyFile) + assertCredentials(t, fmt.Sprintf("data:application/json;base64,%s", + base64.StdEncoding.EncodeToString(b)), keyFile) +} + +func TestNewInvalidClientCredentialsProviderFromKeyFile(t *testing.T) { + p := NewClientCredentialsProviderFromKeyFile("data:application/data,hi") + _, err := p.GetClientCredentials() + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported") +} + +func assertCredentials(t *testing.T, keyfile string, expected *KeyFile) { + p := NewClientCredentialsProviderFromKeyFile(keyfile) + clientCredentials, err := p.GetClientCredentials() + require.NoError(t, err) + assert.Equal(t, expected, clientCredentials) +} diff --git a/oauth2/data_url.go b/oauth2/data_url.go new file mode 100644 index 0000000000..8d711f833a --- /dev/null +++ b/oauth2/data_url.go @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package oauth2 + +import ( + "encoding/base64" + "errors" + "regexp" +) + +var errDataURLInvalid = errors.New("invalid data URL") + +// https://datatracker.ietf.org/doc/html/rfc2397 +var dataURLRegex = regexp.MustCompile("^data:(?P[^;,]+)?(;(?Pcharset=[^;,]+))?" + + "(;(?Pbase64))?,(?P.+)") + +type dataURL struct { + url string + Mimetype string + Data []byte +} + +func newDataURL(url string) (*dataURL, error) { + if !dataURLRegex.Match([]byte(url)) { + return nil, errDataURLInvalid + } + + match := dataURLRegex.FindStringSubmatch(url) + if len(match) != 7 { + return nil, errDataURLInvalid + } + + dataURL := &dataURL{ + url: url, + } + + mimetype := match[dataURLRegex.SubexpIndex("mimetype")] + if mimetype == "" { + mimetype = "text/plain" + } + dataURL.Mimetype = mimetype + + data := match[dataURLRegex.SubexpIndex("data")] + if match[dataURLRegex.SubexpIndex("base64")] == "" { + dataURL.Data = []byte(data) + } else { + data, err := base64.StdEncoding.DecodeString(data) + if err != nil { + return nil, err + } + dataURL.Data = data + } + + return dataURL, nil +} diff --git a/oauth2/data_url_test.go b/oauth2/data_url_test.go new file mode 100644 index 0000000000..8bdc880f82 --- /dev/null +++ b/oauth2/data_url_test.go @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package oauth2 + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewDataURL(t *testing.T) { + rawURL := "data:,test" + url, err := newDataURL(rawURL) + require.NoError(t, err) + assert.Equal(t, "text/plain", url.Mimetype) + assert.Equal(t, "test", string(url.Data)) + + rawURL = "data:;base64," + base64.StdEncoding.EncodeToString([]byte("test")) + url, err = newDataURL(rawURL) + require.NoError(t, err) + assert.Equal(t, "text/plain", url.Mimetype) + assert.Equal(t, "test", string(url.Data)) + + rawURL = "data:application/json,test" + url, err = newDataURL(rawURL) + require.NoError(t, err) + assert.Equal(t, "application/json", url.Mimetype) + assert.Equal(t, "test", string(url.Data)) + + rawURL = "data:application/json;base64," + base64.StdEncoding.EncodeToString([]byte("test")) + url, err = newDataURL(rawURL) + require.NoError(t, err) + assert.Equal(t, "application/json", url.Mimetype) + assert.Equal(t, "test", string(url.Data)) + + rawURL = "data://test" + url, err = newDataURL(rawURL) + require.Nil(t, url) + assert.Error(t, err) + assert.EqualError(t, errDataURLInvalid, err.Error()) +} From da6bb3130a0f3bf27ffa435a62a96dec771943f2 Mon Sep 17 00:00:00 2001 From: Zixuan Liu Date: Wed, 16 Nov 2022 15:21:33 +0800 Subject: [PATCH 2/2] Fix review Signed-off-by: Zixuan Liu --- oauth2/client_credentials_provider.go | 4 ---- oauth2/client_credentials_provider_test.go | 13 ++++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/oauth2/client_credentials_provider.go b/oauth2/client_credentials_provider.go index 402de2c862..5230ca3fee 100644 --- a/oauth2/client_credentials_provider.go +++ b/oauth2/client_credentials_provider.go @@ -19,7 +19,6 @@ package oauth2 import ( "encoding/json" - "fmt" "io/ioutil" "strings" ) @@ -63,9 +62,6 @@ func (k *KeyFileProvider) GetClientCredentials() (*KeyFile, error) { if err != nil { return nil, err } - if url.Mimetype != "application/json" { - return nil, fmt.Errorf("unsupported mimetype: %s", url.Mimetype) - } keyFile = url.Data default: keyFile, err = ioutil.ReadFile(k.KeyFile) diff --git a/oauth2/client_credentials_provider_test.go b/oauth2/client_credentials_provider_test.go index 8296098c14..f47967b6eb 100644 --- a/oauth2/client_credentials_provider_test.go +++ b/oauth2/client_credentials_provider_test.go @@ -53,18 +53,21 @@ func TestNewClientCredentialsProviderFromKeyFile(t *testing.T) { _, err = tmpFile.Write(b) require.NoError(t, err) + jsonData := string(b) + base64Data := base64.StdEncoding.EncodeToString(b) + assertCredentials(t, fmt.Sprintf("file://%s", tmpFile.Name()), keyFile) - assertCredentials(t, fmt.Sprintf("data://%s", string(b)), keyFile) - assertCredentials(t, fmt.Sprintf("data:application/json,%s", string(b)), keyFile) - assertCredentials(t, fmt.Sprintf("data:application/json;base64,%s", - base64.StdEncoding.EncodeToString(b)), keyFile) + assertCredentials(t, fmt.Sprintf("data://%s", jsonData), keyFile) + assertCredentials(t, fmt.Sprintf("data:,%s", jsonData), keyFile) + assertCredentials(t, fmt.Sprintf("data:application/json,%s", jsonData), keyFile) + assertCredentials(t, fmt.Sprintf("data:;base64,%s", base64Data), keyFile) + assertCredentials(t, fmt.Sprintf("data:application/json;base64,%s", base64Data), keyFile) } func TestNewInvalidClientCredentialsProviderFromKeyFile(t *testing.T) { p := NewClientCredentialsProviderFromKeyFile("data:application/data,hi") _, err := p.GetClientCredentials() require.Error(t, err) - assert.Contains(t, err.Error(), "unsupported") } func assertCredentials(t *testing.T, keyfile string, expected *KeyFile) {