Update go dependencies

This commit is contained in:
Manuel de Brito Fontes 2018-05-26 11:27:53 -04:00 committed by Manuel Alejandro de Brito Fontes
parent 15ffb51394
commit bb4d483837
No known key found for this signature in database
GPG key ID: 786136016A8BA02A
1621 changed files with 86368 additions and 284392 deletions

View file

@ -1,133 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed 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 azure
import (
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/Azure/go-autorest/autorest/adal"
)
func TestAzureTokenSource(t *testing.T) {
fakeAccessToken := "fake token 1"
fakeSource := fakeTokenSource{
accessToken: fakeAccessToken,
expiresOn: strconv.FormatInt(time.Now().Add(3600*time.Second).Unix(), 10),
}
cfg := make(map[string]string)
persiter := &fakePersister{cache: make(map[string]string)}
tokenCache := newAzureTokenCache()
tokenSource := newAzureTokenSource(&fakeSource, tokenCache, cfg, persiter)
token, err := tokenSource.Token()
if err != nil {
t.Errorf("failed to retrieve the token form cache: %v", err)
}
wantCacheLen := 1
if len(tokenCache.cache) != wantCacheLen {
t.Errorf("Token() cache length error: got %v, want %v", len(tokenCache.cache), wantCacheLen)
}
if token != tokenCache.cache[azureTokenKey] {
t.Error("Token() returned token != cached token")
}
wantCfg := token2Cfg(token)
persistedCfg := persiter.Cache()
for k, v := range persistedCfg {
if strings.Compare(v, wantCfg[k]) != 0 {
t.Errorf("Token() persisted cfg %s: got %v, want %v", k, v, wantCfg[k])
}
}
fakeSource.accessToken = "fake token 2"
token, err = tokenSource.Token()
if err != nil {
t.Errorf("failed to retrieve the cached token: %v", err)
}
if token.token.AccessToken != fakeAccessToken {
t.Errorf("Token() didn't return the cached token")
}
}
type fakePersister struct {
lock sync.Mutex
cache map[string]string
}
func (p *fakePersister) Persist(cache map[string]string) error {
p.lock.Lock()
defer p.lock.Unlock()
p.cache = map[string]string{}
for k, v := range cache {
p.cache[k] = v
}
return nil
}
func (p *fakePersister) Cache() map[string]string {
ret := map[string]string{}
p.lock.Lock()
defer p.lock.Unlock()
for k, v := range p.cache {
ret[k] = v
}
return ret
}
type fakeTokenSource struct {
expiresOn string
accessToken string
}
func (ts *fakeTokenSource) Token() (*azureToken, error) {
return &azureToken{
token: newFackeAzureToken(ts.accessToken, ts.expiresOn),
clientID: "fake",
tenantID: "fake",
apiserverID: "fake",
}, nil
}
func token2Cfg(token *azureToken) map[string]string {
cfg := make(map[string]string)
cfg[cfgAccessToken] = token.token.AccessToken
cfg[cfgRefreshToken] = token.token.RefreshToken
cfg[cfgClientID] = token.clientID
cfg[cfgTenantID] = token.tenantID
cfg[cfgApiserverID] = token.apiserverID
cfg[cfgExpiresIn] = token.token.ExpiresIn
cfg[cfgExpiresOn] = token.token.ExpiresOn
return cfg
}
func newFackeAzureToken(accessToken string, expiresOn string) adal.Token {
return adal.Token{
AccessToken: accessToken,
RefreshToken: "fake",
ExpiresIn: "3600",
ExpiresOn: expiresOn,
NotBefore: expiresOn,
Resource: "fake",
Type: "fake",
}
}

View file

@ -1,413 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed 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 exec
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"k8s.io/client-go/pkg/apis/clientauthentication"
"k8s.io/client-go/tools/clientcmd/api"
)
func TestCacheKey(t *testing.T) {
c1 := &api.ExecConfig{
Command: "foo-bar",
Args: []string{"1", "2"},
Env: []api.ExecEnvVar{
{Name: "3", Value: "4"},
{Name: "5", Value: "6"},
{Name: "7", Value: "8"},
},
APIVersion: "client.authentication.k8s.io/v1alpha1",
}
c2 := &api.ExecConfig{
Command: "foo-bar",
Args: []string{"1", "2"},
Env: []api.ExecEnvVar{
{Name: "3", Value: "4"},
{Name: "5", Value: "6"},
{Name: "7", Value: "8"},
},
APIVersion: "client.authentication.k8s.io/v1alpha1",
}
c3 := &api.ExecConfig{
Command: "foo-bar",
Args: []string{"1", "2"},
Env: []api.ExecEnvVar{
{Name: "3", Value: "4"},
{Name: "5", Value: "6"},
},
APIVersion: "client.authentication.k8s.io/v1alpha1",
}
key1 := cacheKey(c1)
key2 := cacheKey(c2)
key3 := cacheKey(c3)
if key1 != key2 {
t.Error("key1 and key2 didn't match")
}
if key1 == key3 {
t.Error("key1 and key3 matched")
}
if key2 == key3 {
t.Error("key2 and key3 matched")
}
}
func compJSON(t *testing.T, got, want []byte) {
t.Helper()
gotJSON := &bytes.Buffer{}
wantJSON := &bytes.Buffer{}
if err := json.Indent(gotJSON, got, "", " "); err != nil {
t.Errorf("got invalid JSON: %v", err)
}
if err := json.Indent(wantJSON, want, "", " "); err != nil {
t.Errorf("want invalid JSON: %v", err)
}
g := strings.TrimSpace(gotJSON.String())
w := strings.TrimSpace(wantJSON.String())
if g != w {
t.Errorf("wanted %q, got %q", w, g)
}
}
func TestGetToken(t *testing.T) {
tests := []struct {
name string
config api.ExecConfig
output string
interactive bool
response *clientauthentication.Response
wantInput string
wantToken string
wantExpiry time.Time
wantErr bool
}{
{
name: "basic-request",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "foo-bar"
}
}`,
wantToken: "foo-bar",
},
{
name: "interactive",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
interactive: true,
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {
"interactive": true
}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "foo-bar"
}
}`,
wantToken: "foo-bar",
},
{
name: "response",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
response: &clientauthentication.Response{
Header: map[string][]string{
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
},
Code: 401,
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {
"response": {
"header": {
"WWW-Authenticate": [
"Basic realm=\"Access to the staging site\", charset=\"UTF-8\""
]
},
"code": 401
}
}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "foo-bar"
}
}`,
wantToken: "foo-bar",
},
{
name: "expiry",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "foo-bar",
"expirationTimestamp": "2006-01-02T15:04:05Z"
}
}`,
wantExpiry: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
wantToken: "foo-bar",
},
{
name: "no-group-version",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {}
}`,
output: `{
"kind": "ExecCredential",
"status": {
"token": "foo-bar"
}
}`,
wantErr: true,
},
{
name: "no-status",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1"
}`,
wantErr: true,
},
{
name: "no-token",
config: api.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1alpha1",
},
wantInput: `{
"kind":"ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"spec": {}
}`,
output: `{
"kind": "ExecCredential",
"apiVersion":"client.authentication.k8s.io/v1alpha1",
"status": {}
}`,
wantErr: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := test.config
c.Command = "./testdata/test-plugin.sh"
c.Env = append(c.Env, api.ExecEnvVar{
Name: "TEST_OUTPUT",
Value: test.output,
})
a, err := newAuthenticator(newCache(), &c)
if err != nil {
t.Fatal(err)
}
stderr := &bytes.Buffer{}
a.stderr = stderr
a.interactive = test.interactive
a.environ = func() []string { return nil }
token, err := a.getToken(test.response)
if err != nil {
if !test.wantErr {
t.Errorf("get token %v", err)
}
return
}
if test.wantErr {
t.Fatal("expected error getting token")
}
if token != test.wantToken {
t.Errorf("expected token %q got %q", test.wantToken, token)
}
if !a.exp.Equal(test.wantExpiry) {
t.Errorf("expected expiry %v got %v", test.wantExpiry, a.exp)
}
compJSON(t, stderr.Bytes(), []byte(test.wantInput))
})
}
}
func TestRoundTripper(t *testing.T) {
wantToken := ""
n := time.Now()
now := func() time.Time { return n }
env := []string{""}
environ := func() []string {
s := make([]string, len(env))
copy(s, env)
return s
}
setOutput := func(s string) {
env[0] = "TEST_OUTPUT=" + s
}
handler := func(w http.ResponseWriter, r *http.Request) {
gotToken := ""
parts := strings.Split(r.Header.Get("Authorization"), " ")
if len(parts) > 1 && strings.EqualFold(parts[0], "bearer") {
gotToken = parts[1]
}
if wantToken != gotToken {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
fmt.Fprintln(w, "ok")
}
server := httptest.NewServer(http.HandlerFunc(handler))
c := api.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1alpha1",
}
a, err := newAuthenticator(newCache(), &c)
if err != nil {
t.Fatal(err)
}
a.environ = environ
a.now = now
a.stderr = ioutil.Discard
client := http.Client{
Transport: a.WrapTransport(http.DefaultTransport),
}
get := func(t *testing.T, statusCode int) {
t.Helper()
resp, err := client.Get(server.URL)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != statusCode {
t.Errorf("wanted status %d got %d", statusCode, resp.StatusCode)
}
}
setOutput(`{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "token1"
}
}`)
wantToken = "token1"
get(t, http.StatusOK)
setOutput(`{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "token2"
}
}`)
// Previous token should be cached
get(t, http.StatusOK)
wantToken = "token2"
// Token is still cached, hits unauthorized but causes token to rotate.
get(t, http.StatusUnauthorized)
// Follow up request uses the rotated token.
get(t, http.StatusOK)
setOutput(`{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "token3",
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
}
}`)
wantToken = "token3"
// Token is still cached, hit's unauthorized but causes rotation to token with an expiry.
get(t, http.StatusUnauthorized)
get(t, http.StatusOK)
// Move time forward 2 hours, "token3" is now expired.
n = n.Add(time.Hour * 2)
setOutput(`{
"kind": "ExecCredential",
"apiVersion": "client.authentication.k8s.io/v1alpha1",
"status": {
"token": "token4",
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
}
}`)
wantToken = "token4"
// Old token is expired, should refresh automatically without hitting a 401.
get(t, http.StatusOK)
}

View file

@ -1,497 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed 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 gcp
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"reflect"
"strings"
"sync"
"testing"
"time"
"golang.org/x/oauth2"
)
type fakeOutput struct {
args []string
output string
}
var (
wantCmd []string
// Output for fakeExec, keyed by command
execOutputs = map[string]fakeOutput{
"/default/no/args": {
args: []string{},
output: `{
"access_token": "faketoken",
"token_expiry": "2016-10-31T22:31:09.123000000Z"
}`},
"/default/legacy/args": {
args: []string{"arg1", "arg2", "arg3"},
output: `{
"access_token": "faketoken",
"token_expiry": "2016-10-31T22:31:09.123000000Z"
}`},
"/space in path/customkeys": {
args: []string{"can", "haz", "auth"},
output: `{
"token": "faketoken",
"token_expiry": {
"datetime": "2016-10-31 22:31:09.123"
}
}`},
"missing/tokenkey/noargs": {
args: []string{},
output: `{
"broken": "faketoken",
"token_expiry": {
"datetime": "2016-10-31 22:31:09.123000000Z"
}
}`},
"missing/expirykey/legacyargs": {
args: []string{"split", "on", "whitespace"},
output: `{
"access_token": "faketoken",
"expires": "2016-10-31T22:31:09.123000000Z"
}`},
"invalid expiry/timestamp": {
args: []string{"foo", "--bar", "--baz=abc,def"},
output: `{
"access_token": "faketoken",
"token_expiry": "sometime soon, idk"
}`},
"badjson": {
args: []string{},
output: `{
"access_token": "faketoken",
"token_expiry": "sometime soon, idk"
------
`},
}
)
func fakeExec(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}
func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
// Strip out the leading args used to exec into this function.
gotCmd := os.Args[3]
gotArgs := os.Args[4:]
output, ok := execOutputs[gotCmd]
if !ok {
fmt.Fprintf(os.Stdout, "unexpected call cmd=%q args=%v\n", gotCmd, gotArgs)
os.Exit(1)
} else if !reflect.DeepEqual(output.args, gotArgs) {
fmt.Fprintf(os.Stdout, "call cmd=%q got args %v, want: %v\n", gotCmd, gotArgs, output.args)
os.Exit(1)
}
fmt.Fprintf(os.Stdout, output.output)
os.Exit(0)
}
func Test_isCmdTokenSource(t *testing.T) {
c1 := map[string]string{"cmd-path": "foo"}
if v := isCmdTokenSource(c1); !v {
t.Fatalf("cmd-path present in config (%+v), but got %v", c1, v)
}
c2 := map[string]string{"cmd-args": "foo bar"}
if v := isCmdTokenSource(c2); v {
t.Fatalf("cmd-path not present in config (%+v), but got %v", c2, v)
}
}
func Test_tokenSource_cmd(t *testing.T) {
if _, err := tokenSource(true, map[string]string{}); err == nil {
t.Fatalf("expected error, cmd-args not present in config")
}
c := map[string]string{
"cmd-path": "foo",
"cmd-args": "bar"}
ts, err := tokenSource(true, c)
if err != nil {
t.Fatalf("failed to return cmd token source: %+v", err)
}
if ts == nil {
t.Fatal("returned nil token source")
}
if _, ok := ts.(*commandTokenSource); !ok {
t.Fatalf("returned token source type:(%T) expected:(*commandTokenSource)", ts)
}
}
func Test_tokenSource_cmdCannotBeUsedWithScopes(t *testing.T) {
c := map[string]string{
"cmd-path": "foo",
"scopes": "A,B"}
if _, err := tokenSource(true, c); err == nil {
t.Fatal("expected error when scopes is used with cmd-path")
}
}
func Test_tokenSource_applicationDefaultCredentials_fails(t *testing.T) {
// try to use empty ADC file
fakeTokenFile, err := ioutil.TempFile("", "adctoken")
if err != nil {
t.Fatalf("failed to create fake token file: +%v", err)
}
fakeTokenFile.Close()
defer os.Remove(fakeTokenFile.Name())
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", fakeTokenFile.Name())
defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
if _, err := tokenSource(false, map[string]string{}); err == nil {
t.Fatalf("expected error because specified ADC token file is not a JSON")
}
}
func Test_tokenSource_applicationDefaultCredentials(t *testing.T) {
fakeTokenFile, err := ioutil.TempFile("", "adctoken")
if err != nil {
t.Fatalf("failed to create fake token file: +%v", err)
}
fakeTokenFile.Close()
defer os.Remove(fakeTokenFile.Name())
if err := ioutil.WriteFile(fakeTokenFile.Name(), []byte(`{"type":"service_account"}`), 0600); err != nil {
t.Fatalf("failed to write to fake token file: %+v", err)
}
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", fakeTokenFile.Name())
defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
ts, err := tokenSource(false, map[string]string{})
if err != nil {
t.Fatalf("failed to get a token source: %+v", err)
}
if ts == nil {
t.Fatal("returned nil token source")
}
}
func Test_parseScopes(t *testing.T) {
cases := []struct {
in map[string]string
out []string
}{
{
map[string]string{},
[]string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email"},
},
{
map[string]string{"scopes": ""},
[]string{},
},
{
map[string]string{"scopes": "A,B,C"},
[]string{"A", "B", "C"},
},
}
for _, c := range cases {
got := parseScopes(c.in)
if !reflect.DeepEqual(got, c.out) {
t.Errorf("expected=%v, got=%v", c.out, got)
}
}
}
func errEquiv(got, want error) bool {
if got == want {
return true
}
if got != nil && want != nil {
return strings.Contains(got.Error(), want.Error())
}
return false
}
func TestCmdTokenSource(t *testing.T) {
execCommand = fakeExec
fakeExpiry := time.Date(2016, 10, 31, 22, 31, 9, 123000000, time.UTC)
customFmt := "2006-01-02 15:04:05.999999999"
tests := []struct {
name string
gcpConfig map[string]string
tok *oauth2.Token
newErr, tokenErr error
}{
{
"default",
map[string]string{
"cmd-path": "/default/no/args",
},
&oauth2.Token{
AccessToken: "faketoken",
TokenType: "Bearer",
Expiry: fakeExpiry,
},
nil,
nil,
},
{
"default legacy args",
map[string]string{
"cmd-path": "/default/legacy/args arg1 arg2 arg3",
},
&oauth2.Token{
AccessToken: "faketoken",
TokenType: "Bearer",
Expiry: fakeExpiry,
},
nil,
nil,
},
{
"custom keys",
map[string]string{
"cmd-path": "/space in path/customkeys",
"cmd-args": "can haz auth",
"token-key": "{.token}",
"expiry-key": "{.token_expiry.datetime}",
"time-fmt": customFmt,
},
&oauth2.Token{
AccessToken: "faketoken",
TokenType: "Bearer",
Expiry: fakeExpiry,
},
nil,
nil,
},
{
"missing cmd",
map[string]string{
"cmd-path": "",
},
nil,
fmt.Errorf("missing access token cmd"),
nil,
},
{
"missing token-key",
map[string]string{
"cmd-path": "missing/tokenkey/noargs",
"token-key": "{.token}",
},
nil,
nil,
fmt.Errorf("error parsing token-key %q", "{.token}"),
},
{
"missing expiry-key",
map[string]string{
"cmd-path": "missing/expirykey/legacyargs split on whitespace",
"expiry-key": "{.expiry}",
},
nil,
nil,
fmt.Errorf("error parsing expiry-key %q", "{.expiry}"),
},
{
"invalid expiry timestamp",
map[string]string{
"cmd-path": "invalid expiry/timestamp",
"cmd-args": "foo --bar --baz=abc,def",
},
&oauth2.Token{
AccessToken: "faketoken",
TokenType: "Bearer",
Expiry: time.Time{},
},
nil,
nil,
},
{
"bad JSON",
map[string]string{
"cmd-path": "badjson",
},
nil,
nil,
fmt.Errorf("invalid character '-' after object key:value pair"),
},
}
for _, tc := range tests {
provider, err := newGCPAuthProvider("", tc.gcpConfig, nil /* persister */)
if !errEquiv(err, tc.newErr) {
t.Errorf("%q newGCPAuthProvider error: got %v, want %v", tc.name, err, tc.newErr)
continue
}
if err != nil {
continue
}
ts := provider.(*gcpAuthProvider).tokenSource.(*cachedTokenSource).source.(*commandTokenSource)
wantCmd = append([]string{ts.cmd}, ts.args...)
tok, err := ts.Token()
if !errEquiv(err, tc.tokenErr) {
t.Errorf("%q Token() error: got %v, want %v", tc.name, err, tc.tokenErr)
}
if !reflect.DeepEqual(tok, tc.tok) {
t.Errorf("%q Token() got %v, want %v", tc.name, tok, tc.tok)
}
}
}
type fakePersister struct {
lk sync.Mutex
cache map[string]string
}
func (f *fakePersister) Persist(cache map[string]string) error {
f.lk.Lock()
defer f.lk.Unlock()
f.cache = map[string]string{}
for k, v := range cache {
f.cache[k] = v
}
return nil
}
func (f *fakePersister) read() map[string]string {
ret := map[string]string{}
f.lk.Lock()
defer f.lk.Unlock()
for k, v := range f.cache {
ret[k] = v
}
return ret
}
type fakeTokenSource struct {
token *oauth2.Token
err error
}
func (f *fakeTokenSource) Token() (*oauth2.Token, error) {
return f.token, f.err
}
func TestCachedTokenSource(t *testing.T) {
tok := &oauth2.Token{AccessToken: "fakeaccesstoken"}
persister := &fakePersister{}
source := &fakeTokenSource{
token: tok,
err: nil,
}
cache := map[string]string{
"foo": "bar",
"baz": "bazinga",
}
ts, err := newCachedTokenSource("fakeaccesstoken", "", persister, source, cache)
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
_, err := ts.Token()
if err != nil {
t.Errorf("unexpected error: %s", err)
}
wg.Done()
}()
}
wg.Wait()
cache["access-token"] = "fakeaccesstoken"
cache["expiry"] = tok.Expiry.Format(time.RFC3339Nano)
if got := persister.read(); !reflect.DeepEqual(got, cache) {
t.Errorf("got cache %v, want %v", got, cache)
}
}
type MockTransport struct {
res *http.Response
}
func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return t.res, nil
}
func TestClearingCredentials(t *testing.T) {
fakeExpiry := time.Now().Add(time.Hour)
cache := map[string]string{
"access-token": "fakeToken",
"expiry": fakeExpiry.String(),
}
cts := cachedTokenSource{
source: nil,
accessToken: cache["access-token"],
expiry: fakeExpiry,
persister: nil,
cache: nil,
}
tests := []struct {
name string
res http.Response
cache map[string]string
}{
{
"Unauthorized",
http.Response{StatusCode: 401},
make(map[string]string),
},
{
"Authorized",
http.Response{StatusCode: 200},
cache,
},
}
persister := &fakePersister{}
req := http.Request{Header: http.Header{}}
for _, tc := range tests {
authProvider := gcpAuthProvider{&cts, persister}
fakeTransport := MockTransport{&tc.res}
transport := (authProvider.WrapTransport(&fakeTransport))
persister.Persist(cache)
transport.RoundTrip(&req)
if got := persister.read(); !reflect.DeepEqual(got, tc.cache) {
t.Errorf("got cache %v, want %v", got, tc.cache)
}
}
}

View file

@ -1,138 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed 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 oidc
import (
"encoding/base64"
"encoding/json"
"fmt"
"testing"
"time"
)
func TestJSONTime(t *testing.T) {
data := `{
"t1": 1493851263,
"t2": 1.493851263e9
}`
var v struct {
T1 jsonTime `json:"t1"`
T2 jsonTime `json:"t2"`
}
if err := json.Unmarshal([]byte(data), &v); err != nil {
t.Fatal(err)
}
wantT1 := time.Unix(1493851263, 0)
wantT2 := time.Unix(1493851263, 0)
gotT1 := time.Time(v.T1)
gotT2 := time.Time(v.T2)
if !wantT1.Equal(gotT1) {
t.Errorf("t1 value: wanted %s got %s", wantT1, gotT1)
}
if !wantT2.Equal(gotT2) {
t.Errorf("t2 value: wanted %s got %s", wantT2, gotT2)
}
}
func encodeJWT(header, payload, sig string) string {
e := func(s string) string {
return base64.RawURLEncoding.EncodeToString([]byte(s))
}
return e(header) + "." + e(payload) + "." + e(sig)
}
func TestExpired(t *testing.T) {
now := time.Now()
nowFunc := func() time.Time { return now }
tests := []struct {
name string
idToken string
wantErr bool
wantExpired bool
}{
{
name: "valid",
idToken: encodeJWT(
"{}",
fmt.Sprintf(`{"exp":%d}`, now.Add(time.Hour).Unix()),
"blah", // signature isn't veified.
),
},
{
name: "expired",
idToken: encodeJWT(
"{}",
fmt.Sprintf(`{"exp":%d}`, now.Add(-time.Hour).Unix()),
"blah", // signature isn't veified.
),
wantExpired: true,
},
{
name: "bad exp claim",
idToken: encodeJWT(
"{}",
`{"exp":"foobar"}`,
"blah", // signature isn't veified.
),
wantErr: true,
},
{
name: "not an id token",
idToken: "notanidtoken",
wantErr: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
valid, err := idTokenExpired(nowFunc, test.idToken)
if err != nil {
if !test.wantErr {
t.Errorf("parse error: %v", err)
}
return
}
if test.wantExpired == valid {
t.Errorf("wanted expired %t, got %t", test.wantExpired, !valid)
}
})
}
}
func TestClientCache(t *testing.T) {
cache := newClientCache()
if _, ok := cache.getClient("issuer1", "id1"); ok {
t.Fatalf("got client before putting one in the cache")
}
cli1 := new(oidcAuthProvider)
cli2 := new(oidcAuthProvider)
gotcli := cache.setClient("issuer1", "id1", cli1)
if cli1 != gotcli {
t.Fatalf("set first client and got a different one")
}
gotcli = cache.setClient("issuer1", "id1", cli2)
if cli1 != gotcli {
t.Fatalf("set a second client and didn't get the first")
}
}

View file

@ -1,116 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed 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 openstack
import (
"math/rand"
"net/http"
"testing"
"time"
)
// testTokenGetter is a simple random token getter.
type testTokenGetter struct{}
const LetterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = LetterBytes[rand.Intn(len(LetterBytes))]
}
return string(b)
}
func (*testTokenGetter) Token() (string, error) {
return RandStringBytes(32), nil
}
// testRoundTripper is mocked roundtripper which responds with unauthorized when
// there is no authorization header, otherwise returns status ok.
type testRoundTripper struct{}
func (trt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
authHeader := req.Header.Get("Authorization")
if authHeader == "" || authHeader == "Bearer " {
return &http.Response{
StatusCode: http.StatusUnauthorized,
}, nil
}
return &http.Response{StatusCode: http.StatusOK}, nil
}
func TestOpenstackAuthProvider(t *testing.T) {
trt := &tokenRoundTripper{
RoundTripper: &testRoundTripper{},
}
tests := []struct {
name string
ttl time.Duration
interval time.Duration
same bool
}{
{
name: "normal",
ttl: 2 * time.Second,
interval: 1 * time.Second,
same: true,
},
{
name: "expire",
ttl: 1 * time.Second,
interval: 2 * time.Second,
same: false,
},
}
for _, test := range tests {
trt.tokenGetter = &cachedGetter{
tokenGetter: &testTokenGetter{},
ttl: test.ttl,
}
req, err := http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
if err != nil {
t.Errorf("failed to new request: %s", err)
}
trt.RoundTrip(req)
header := req.Header.Get("Authorization")
if header == "" {
t.Errorf("expect to see token in header, but is absent")
}
time.Sleep(test.interval)
req, err = http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
if err != nil {
t.Errorf("failed to new request: %s", err)
}
trt.RoundTrip(req)
newHeader := req.Header.Get("Authorization")
if newHeader == "" {
t.Errorf("expect to see token in header, but is absent")
}
same := newHeader == header
if same != test.same {
t.Errorf("expect to get %t when compare header, but saw %t", test.same, same)
}
}
}