Skip to content

Commit

Permalink
Add data url format to read the key file
Browse files Browse the repository at this point in the history
Signed-off-by: Zixuan Liu <nodeces@gmail.com>
  • Loading branch information
nodece committed Nov 2, 2022
1 parent 44b64aa commit 10e4664
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 3 deletions.
17 changes: 14 additions & 3 deletions oauth2/client_credentials_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ package oauth2

import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
)

const (
FILE = "file://"
DATA = "data://"
FILE = "file://"
DATA = "data://"
DATA_URL = "data:"
)

type KeyFileProvider struct {
Expand Down Expand Up @@ -55,8 +57,17 @@ func (k *KeyFileProvider) GetClientCredentials() (*KeyFile, error) {
case strings.HasPrefix(k.KeyFile, FILE):
filename := strings.TrimPrefix(k.KeyFile, FILE)
keyFile, err = ioutil.ReadFile(filename)
case strings.HasPrefix(k.KeyFile, "data://"):
case strings.HasPrefix(k.KeyFile, DATA):
keyFile = []byte(strings.TrimPrefix(k.KeyFile, DATA))
case strings.HasPrefix(k.KeyFile, DATA_URL):
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)
}
Expand Down
74 changes: 74 additions & 0 deletions oauth2/client_credentials_provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io/ioutil"
"os"
"testing"
)

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)
}
70 changes: 70 additions & 0 deletions oauth2/data_url.go
Original file line number Diff line number Diff line change
@@ -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<mimetype>[^;,]+)?(;(?P<charset>charset=[^;,]+))?" +
"(;(?P<base64>base64))?,(?P<data>.+)")

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
}
58 changes: 58 additions & 0 deletions oauth2/data_url_test.go
Original file line number Diff line number Diff line change
@@ -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())
}

0 comments on commit 10e4664

Please sign in to comment.