github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/api/cloudcontroller/wrapper/kubernetes_authentication_test.go (about)

     1  package wrapper_test
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"encoding/pem"
     7  	"errors"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"strings"
    12  	"time"
    13  
    14  	"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
    15  	"code.cloudfoundry.org/cli/api/cloudcontroller"
    16  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes"
    17  	"code.cloudfoundry.org/cli/api/cloudcontroller/wrapper"
    18  	"code.cloudfoundry.org/cli/command/commandfakes"
    19  	"code.cloudfoundry.org/cli/integration/helpers"
    20  
    21  	"github.com/SermoDigital/jose/crypto"
    22  	"github.com/SermoDigital/jose/jws"
    23  	. "github.com/onsi/ginkgo"
    24  	. "github.com/onsi/gomega"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
    27  	"k8s.io/client-go/tools/clientcmd/api"
    28  )
    29  
    30  const (
    31  	clientCertData = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJVk9iMUFIckxNUjh3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRFd01EVXhOVEExTURsYUZ3MHlNakV3TURVeE5UQTFNVEZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXJrdWxLbS9qTTJhZWZsdjkKK00zQk9Jc2QvVXZrRTBONGhWb3hSeWRBbE0xQXhWd3REYUdzL3dmUzRzb0xuNHJENTF3UE1SRlNJaitwSzdGYQprRGdaR0x4UFhrai96UkZOTzcvU3J2RHYwVGxjYjJENzNCS21qaXArQ2hBWkpQdWhMQlY2VnlTN0pXSWhOM1lOCktyamR5TnB5MHN3SjI1TW9CbW1saUpFc3V2dCtDaEhseERqWE9KenF1U2owa1hPQVVsWUFTN1dKK09JMU9HbzQKUjcvdHdHZlFTNW9oYXpRVVlDR2lZSllYcjVRNkVKTmJOVVI0RjdpRSthY1I5Rm9GNnNKSmkrQStET1VDUFFSKwptbjQ5Zm1pcFVHSGtMc3BicTNFZ0FEME40VW5jcmIyeUJEMFNVTmdLQmJjclY1S2hybFA2SzkwNkY5NEpubzNHCm1Id1JwUUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JUV2VNZ1ZBRkRhbWcraDRqS3hoRUh2Q1l5egp5akFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUUxMWWFXQTRva1M2b3ZEWjQ1Z28ybkVZdUR4MklmRXZwYnh3CkNmYkFTNDY4M3lLT3FiYVBHOEpTVGhSbkh3TWlMcVBrbGFsdkJvV2R3aFB5Vkk0d2tVNHI4Y2c0UEpxNEZwWnQKVkNUQzFPZWVwRGpTMFpVQjRwSDVIZVlNQUxqSDBqcFV3RU96djFpaEtid05IMHFoZ2pGeUNTdld5TG9oZHdzbApJWXIvV1NEZm50NlBETC84TjFpcEJJbEN5Z1JHVGdoSFhPemhHUklPWG4rYWVOR29yWm9YWm0xbHErc1hyUnc5CktNdVZhRmdhaWVjSm0vbytyemFFSG9VZjRYOERKeVNubmVTa3ViaEx6ZERNc2o5eEs1cEJpdFgvaDlQMUQrMkcKeW5rcWdJVTJSWTM0SjBRcnU4Z0syNlJVT2pOcHIvRWJHQ0dUQUxiMXJnSDM0K2NFdlE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
    32  	clientKeyData  = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3VTNlVxYitNelpwNSsKVy8zNHpjRTRpeDM5UytRVFEzaUZXakZISjBDVXpVREZYQzBOb2F6L0I5TGl5Z3VmaXNQblhBOHhFVklpUDZrcgpzVnFRT0JrWXZFOWVTUC9ORVUwN3Y5S3U4Ty9ST1Z4dllQdmNFcWFPS240S0VCa2srNkVzRlhwWEpMc2xZaUUzCmRnMHF1TjNJMm5MU3pBbmJreWdHYWFXSWtTeTYrMzRLRWVYRU9OYzRuT3E1S1BTUmM0QlNWZ0JMdFluNDRqVTQKYWpoSHYrM0FaOUJMbWlGck5CUmdJYUpnbGhldmxEb1FrMXMxUkhnWHVJVDVweEgwV2dYcXdrbUw0RDRNNVFJOQpCSDZhZmoxK2FLbFFZZVF1eWx1cmNTQUFQUTNoU2R5dHZiSUVQUkpRMkFvRnR5dFhrcUd1VS9vcjNUb1gzZ21lCmpjYVlmQkdsQWdNQkFBRUNnZ0VBZG80WndLM3VteTM0TFBjaDM3VUU4eE1keVFkd0VmSlk3a3dWTE5MMFNNTDgKaGNKWEd1aVlKYmtLcHh6TG55L2laV0xuS25jZnFSQW9ZQUg1R2hRdWJmYlkvY2NseURVMmxhZTdCU2Y1MkJUdQpYUXhaQks3aS85ekRjdERVYWFXSFVkY2lLbGhmdStQdHVDM2ljdWJnWlJqQjljUzRCOVVtNm9XK0JSREtuandICkduQ0lEZlNNQWt4VXdTaUwwa2NXelNpZ1BYMVN3UHcxOEZvZWgzTmJEd1VXTHhxUWZLVThydVlSTUsxYUg5M3cKcjFtbjlDWUwvd0hiVWRqcmtZMlIxTjVUR21ab2Vldm5qUXgyQVc2NkYzdEg1cGg4RTF6TEFQVTl4TFdRTW9KcwpXM0gzSTdUaEYvRnJuNERQa3hQbThUUVVhQUdvQ09SSWFUQkN5VlgxQVFLQmdRREkxbkRmNWYySHdHaldrTStpCk9YbGE1R1VnRUtXaGZpeGhidE5OclNpMDU5VnZQUEJwNXdtbGQzMHJKUDhWem8vbnFnUW5ISmpmaEQ2Y3NSMTQKL2VlMHZ1Um0zYzZwZzMrODdwOHhWY3lLNHhDd0JmdFFuMGRZWWFLMkRMOEtYb0liYThpN09EQmFoNW5OZWQwcgpKa1RPcE5NRGRkL0p0bEpPZ25jRXBlUk9oUUtCZ1FEZUt1L0R1MXU1QVR3Y3p5STRXOWV1L1YwTXRwMHdqM3RpClF2MmpObW83QU1zS3BwK0ZKVDFqWFhUKzZCTm02OWpxUVJwdlAyd2RhVUdqV1dLa1lHVEVpbUZCc2ZuKzJDOFAKOEc3Uk50YWpRdEV2QlR1ZDZPN0tZUkFoTU56dm9RcDkrZmJKY1ZsRG13Nlk0bUYzUTJXS3NmZU51TGtpY3VqNQpYVFV1ekVMd29RS0JnUUREU0IvQTFYVEx4cjhwd3V6aHBGam5sQ1R3Skwrb1kzTHIya01EeUZkSWNCUU1jWWlpCnNNK2tZS2NJaUpTdnM0WWhrQ014bEpEZzVVbXNPbHVhQmVpQ3l3cHpLMEdEZWlWK285ZU90UXFLRVhkc2NLU0oKSkJiUFRVQlZHOWUyVVdiWkd0aTNrazhSOThBSkYzR0NQMWV3Um53WFpVb1FiSU5qYTJBbTJOZEJzUUtCZ0Q4eApOVXVXTWl1NE56SDJsTVExRTI4NXI4cmE4bkVLanN6UFF6ZTJWWmI4emNQMHl2RGpPOGZVb0YrVkFWZklBOFgxCnlLQVdDUm1BZytRRG03UW5tdUh3Zm1OaVRUcDRvVUpHWUM3d0N6TWE0VWNmbE9xQWc5TmFzbXpPYWpsYXRCSkwKRkRBT0pwYTlOdlN6aDRlVnl2OGRTYzJzMmpQN1BWc1ljUFVqc25LaEFvR0JBSy9kQjlnVEFpME5nczVmaVNtWQovWkp3Yk52MjcyTHdKbWV4Vit2eWtjN3J5LzRraTRQb2xRd1BHNzQ5eFZ0T2NNc2FhRlVNMVVkclN2NlIwbjlkCmpTbXhCeTl2YWdzc1FmVDNSc3BvUUJKM0w5YWxiNHM2V2ZtUEpzNkFrQkhIZHNpVXFaaElYT2J2WE1lQ0k2aVMKOTQ2R0toekFxMlVGbjhFUGxXaFVNeEFiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
    33  )
    34  
    35  var _ = Describe("KubernetesAuthentication", func() {
    36  	var (
    37  		k8sAuthWrapper    *wrapper.KubernetesAuthentication
    38  		config            *commandfakes.FakeConfig
    39  		k8sConfigGetter   *v7actionfakes.FakeKubernetesConfigGetter
    40  		wrappedConnection *ccv3fakes.FakeConnectionWrapper
    41  		req               *cloudcontroller.Request
    42  		resp              *cloudcontroller.Response
    43  		kubeConfig        *api.Config
    44  		makeErr           error
    45  	)
    46  
    47  	BeforeEach(func() {
    48  		kubeConfig = &api.Config{
    49  			Kind:       "Config",
    50  			APIVersion: "v1",
    51  			Clusters: map[string]*api.Cluster{
    52  				"my-cluster": {
    53  					Server: "https://example.org",
    54  				},
    55  			},
    56  			Contexts: map[string]*api.Context{
    57  				"my-context": {
    58  					Cluster:   "my-cluster",
    59  					AuthInfo:  "my-auth-info",
    60  					Namespace: "my-namespace",
    61  				},
    62  			},
    63  			CurrentContext: "my-context",
    64  			AuthInfos:      map[string]*api.AuthInfo{},
    65  		}
    66  
    67  		k8sConfigGetter = new(v7actionfakes.FakeKubernetesConfigGetter)
    68  		k8sConfigGetter.GetReturns(kubeConfig, nil)
    69  
    70  		config = new(commandfakes.FakeConfig)
    71  		config.CurrentUserNameReturns("auth-test", nil)
    72  
    73  		wrappedConnection = new(ccv3fakes.FakeConnectionWrapper)
    74  
    75  		httpReq, err := http.NewRequest(http.MethodPost, "", strings.NewReader("hello"))
    76  		Expect(err).NotTo(HaveOccurred())
    77  		req = cloudcontroller.NewRequest(httpReq, nil)
    78  
    79  		resp = &cloudcontroller.Response{
    80  			HTTPResponse: &http.Response{
    81  				StatusCode: http.StatusTeapot,
    82  			},
    83  		}
    84  	})
    85  
    86  	JustBeforeEach(func() {
    87  		k8sAuthWrapper = wrapper.NewKubernetesAuthentication(config, k8sConfigGetter)
    88  		k8sAuthWrapper.Wrap(wrappedConnection)
    89  
    90  		makeErr = k8sAuthWrapper.Make(req, resp)
    91  	})
    92  
    93  	When("getting the k8s config fails", func() {
    94  		BeforeEach(func() {
    95  			k8sConfigGetter.GetReturns(nil, errors.New("boom!"))
    96  		})
    97  
    98  		It("returns the error", func() {
    99  			Expect(makeErr).To(MatchError("boom!"))
   100  		})
   101  	})
   102  
   103  	When("no user is set in the config", func() {
   104  		BeforeEach(func() {
   105  			config.CurrentUserNameReturns("", nil)
   106  		})
   107  
   108  		It("errors", func() {
   109  			Expect(makeErr).To(MatchError(ContainSubstring("current user not set")))
   110  		})
   111  	})
   112  
   113  	When("there is an error getting the current user from the config", func() {
   114  		BeforeEach(func() {
   115  			config.CurrentUserNameReturns("", errors.New("boom"))
   116  		})
   117  
   118  		It("errors", func() {
   119  			Expect(makeErr).To(MatchError(ContainSubstring("boom")))
   120  		})
   121  	})
   122  
   123  	When("the chosen kubernetes auth info is not present in kubeconfig", func() {
   124  		BeforeEach(func() {
   125  			config.CurrentUserNameReturns("not-present", nil)
   126  		})
   127  
   128  		It("errors", func() {
   129  			Expect(makeErr).To(MatchError(ContainSubstring(`auth info "not-present" does not exist`)))
   130  		})
   131  	})
   132  
   133  	checkCalls := func() *cloudcontroller.Request {
   134  		Expect(makeErr).NotTo(HaveOccurred())
   135  		Expect(wrappedConnection.MakeCallCount()).To(Equal(1))
   136  
   137  		actualReq, actualResp := wrappedConnection.MakeArgsForCall(0)
   138  		Expect(actualResp.HTTPResponse).To(HaveHTTPStatus(http.StatusTeapot))
   139  
   140  		body, err := ioutil.ReadAll(actualReq.Body)
   141  		Expect(err).NotTo(HaveOccurred())
   142  		Expect(string(body)).To(Equal("hello"))
   143  
   144  		return actualReq
   145  	}
   146  
   147  	checkBearerTokenInAuthHeader := func() {
   148  		actualReq := checkCalls()
   149  
   150  		token, err := jws.ParseJWTFromRequest(actualReq.Request)
   151  		Expect(err).NotTo(HaveOccurred())
   152  		Expect(token.Validate(keyPair.Public(), crypto.SigningMethodRS256)).To(Succeed())
   153  
   154  		claims := token.Claims()
   155  		Expect(claims).To(HaveKeyWithValue("another", "thing"))
   156  	}
   157  
   158  	checkClientCertInAuthHeader := func() {
   159  		actualReq := checkCalls()
   160  
   161  		Expect(actualReq.Header).To(HaveKeyWithValue("Authorization", ConsistOf(HavePrefix("ClientCert "))))
   162  
   163  		certAndKeyPEMBase64 := actualReq.Header.Get("Authorization")[11:]
   164  		certAndKeyPEM, err := base64.StdEncoding.DecodeString(certAndKeyPEMBase64)
   165  		Expect(err).NotTo(HaveOccurred())
   166  
   167  		cert, rest := pem.Decode(certAndKeyPEM)
   168  		Expect(cert.Type).To(Equal(pemDecodeKubeConfigCertData(clientCertData).Type))
   169  		Expect(cert.Bytes).To(Equal(pemDecodeKubeConfigCertData(clientCertData).Bytes))
   170  
   171  		var key *pem.Block
   172  		key, rest = pem.Decode(rest)
   173  		Expect(key.Bytes).To(Equal(pemDecodeKubeConfigCertData(clientKeyData).Bytes))
   174  
   175  		Expect(rest).To(BeEmpty())
   176  	}
   177  
   178  	Describe("auth-provider", func() {
   179  		var token []byte
   180  
   181  		BeforeEach(func() {
   182  			jwt := jws.NewJWT(jws.Claims{
   183  				"exp":     time.Now().Add(time.Hour).Unix(),
   184  				"another": "thing",
   185  			}, crypto.SigningMethodRS256)
   186  			var err error
   187  			token, err = jwt.Serialize(keyPair)
   188  			Expect(err).NotTo(HaveOccurred())
   189  
   190  			kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   191  				AuthProvider: &api.AuthProviderConfig{
   192  					Name: "oidc",
   193  					Config: map[string]string{
   194  						"id-token":       string(token),
   195  						"idp-issuer-url": "-",
   196  						"client-id":      "-",
   197  					},
   198  				},
   199  			}
   200  		})
   201  
   202  		It("uses the auth-provider to generate the Bearer token", func() {
   203  			checkBearerTokenInAuthHeader()
   204  		})
   205  	})
   206  
   207  	Describe("client certs", func() {
   208  		var (
   209  			certFilePath string
   210  			keyFilePath  string
   211  		)
   212  
   213  		BeforeEach(func() {
   214  			certFilePath = writeToFile(clientCertData)
   215  			keyFilePath = writeToFile(clientKeyData)
   216  		})
   217  
   218  		AfterEach(func() {
   219  			Expect(os.RemoveAll(certFilePath)).To(Succeed())
   220  			Expect(os.RemoveAll(keyFilePath)).To(Succeed())
   221  		})
   222  
   223  		When("inline cert and key are provided", func() {
   224  			BeforeEach(func() {
   225  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   226  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   227  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   228  				}
   229  			})
   230  
   231  			It("puts concatenated client ceritificate and key data into the Authorization header", func() {
   232  				checkClientCertInAuthHeader()
   233  			})
   234  		})
   235  
   236  		When("cert and key are provided in files", func() {
   237  			BeforeEach(func() {
   238  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   239  					ClientCertificate: certFilePath,
   240  					ClientKey:         keyFilePath,
   241  				}
   242  			})
   243  
   244  			It("puts concatenated client ceritificate and key data into the Authorization header", func() {
   245  				checkClientCertInAuthHeader()
   246  			})
   247  
   248  			When("cert file cannot be read", func() {
   249  				BeforeEach(func() {
   250  					Expect(os.Remove(certFilePath)).To(Succeed())
   251  				})
   252  
   253  				It("returns an error", func() {
   254  					Expect(makeErr).To(MatchError(ContainSubstring(certFilePath)))
   255  				})
   256  			})
   257  
   258  			When("key file cannot be read", func() {
   259  				BeforeEach(func() {
   260  					Expect(os.Remove(keyFilePath)).To(Succeed())
   261  				})
   262  
   263  				It("returns an error", func() {
   264  					Expect(makeErr).To(MatchError(ContainSubstring(keyFilePath)))
   265  				})
   266  			})
   267  		})
   268  
   269  		When("file and inline cert is provided", func() {
   270  			BeforeEach(func() {
   271  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   272  					ClientCertificate:     certFilePath,
   273  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   274  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   275  				}
   276  			})
   277  
   278  			It("complains about invalid configuration", func() {
   279  				Expect(makeErr).To(MatchError(ContainSubstring("client-cert-data and client-cert are both specified")))
   280  			})
   281  		})
   282  
   283  		When("file and inline key is provided", func() {
   284  			BeforeEach(func() {
   285  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   286  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   287  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   288  					ClientKey:             keyFilePath,
   289  				}
   290  			})
   291  
   292  			It("complains about invalid configuration", func() {
   293  				Expect(makeErr).To(MatchError(ContainSubstring("client-key-data and client-key are both specified")))
   294  			})
   295  		})
   296  
   297  		When("inline cert and key file are provided", func() {
   298  			BeforeEach(func() {
   299  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   300  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   301  					ClientKey:             keyFilePath,
   302  				}
   303  			})
   304  
   305  			It("uses the inline key", func() {
   306  				checkClientCertInAuthHeader()
   307  			})
   308  		})
   309  
   310  		When("cert file and inline key are provided", func() {
   311  			BeforeEach(func() {
   312  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   313  					ClientCertificate: certFilePath,
   314  					ClientKeyData:     []byte(base64Decode(clientKeyData)),
   315  				}
   316  			})
   317  
   318  			It("uses the inline key", func() {
   319  				checkClientCertInAuthHeader()
   320  			})
   321  		})
   322  	})
   323  
   324  	Describe("exec", func() {
   325  		BeforeEach(func() {
   326  			kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   327  				Exec: &api.ExecConfig{
   328  					APIVersion:      "client.authentication.k8s.io/v1beta1",
   329  					InteractiveMode: "Never",
   330  					Command:         "echo",
   331  				},
   332  			}
   333  		})
   334  
   335  		When("the command returns a token", func() {
   336  			BeforeEach(func() {
   337  				kubeConfig.AuthInfos["auth-test"].Exec.Args = []string{execCredential(&clientauthenticationv1beta1.ExecCredentialStatus{
   338  					Token: "a-token",
   339  				})}
   340  			})
   341  
   342  			It("uses the exec command to generate the Bearer token", func() {
   343  				helpers.SkipIfWindows() // We're getting "plugin returned version client.authentication.k8s.io/__internal" on Windows. This issue is unresolved in upstream library.
   344  				Expect(makeErr).NotTo(HaveOccurred())
   345  				Expect(wrappedConnection.MakeCallCount()).To(Equal(1))
   346  
   347  				actualReq, actualResp := wrappedConnection.MakeArgsForCall(0)
   348  				Expect(actualResp.HTTPResponse).To(HaveHTTPStatus(http.StatusTeapot))
   349  				Expect(actualReq.Header.Get("Authorization")).To(Equal("Bearer a-token"))
   350  			})
   351  		})
   352  
   353  		When("the command returns a client cert and key", func() {
   354  			BeforeEach(func() {
   355  				kubeConfig.AuthInfos["auth-test"].Exec.Args = []string{execCredential(&clientauthenticationv1beta1.ExecCredentialStatus{
   356  					ClientCertificateData: base64Decode(clientCertData),
   357  					ClientKeyData:         base64Decode(clientKeyData),
   358  				})}
   359  			})
   360  
   361  			It("uses the exec command to generate client certs", func() {
   362  				checkClientCertInAuthHeader()
   363  			})
   364  		})
   365  	})
   366  
   367  	Describe("tokens provided in config", func() {
   368  		var token []byte
   369  
   370  		BeforeEach(func() {
   371  			jwt := jws.NewJWT(jws.Claims{
   372  				"exp":     time.Now().Add(time.Hour).Unix(),
   373  				"another": "thing",
   374  			}, crypto.SigningMethodRS256)
   375  			var err error
   376  			token, err = jwt.Serialize(keyPair)
   377  			Expect(err).NotTo(HaveOccurred())
   378  		})
   379  
   380  		Context("inline tokens", func() {
   381  			BeforeEach(func() {
   382  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   383  					Token: string(token),
   384  				}
   385  			})
   386  
   387  			It("inserts the token in the authorization header", func() {
   388  				checkBearerTokenInAuthHeader()
   389  			})
   390  		})
   391  
   392  		Context("token file paths", func() {
   393  			var tokenFilePath string
   394  
   395  			BeforeEach(func() {
   396  				tokenFile, err := ioutil.TempFile("", "")
   397  				Expect(err).NotTo(HaveOccurred())
   398  				defer tokenFile.Close()
   399  				_, err = tokenFile.Write(token)
   400  				Expect(err).NotTo(HaveOccurred())
   401  				tokenFilePath = tokenFile.Name()
   402  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   403  					TokenFile: tokenFilePath,
   404  				}
   405  			})
   406  
   407  			AfterEach(func() {
   408  				Expect(os.RemoveAll(tokenFilePath)).To(Succeed())
   409  			})
   410  
   411  			It("inserts the token in the authorization header", func() {
   412  				checkBearerTokenInAuthHeader()
   413  			})
   414  		})
   415  
   416  		When("both file and inline token are provided", func() {
   417  			BeforeEach(func() {
   418  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   419  					Token:     string(token),
   420  					TokenFile: "some-path",
   421  				}
   422  			})
   423  
   424  			It("the inline token takes precedence", func() {
   425  				checkBearerTokenInAuthHeader()
   426  			})
   427  		})
   428  	})
   429  })
   430  
   431  func pemDecodeKubeConfigCertData(data string) *pem.Block {
   432  	decodedData, err := base64.StdEncoding.DecodeString(data)
   433  	Expect(err).NotTo(HaveOccurred())
   434  	pemDecodedBlock, rest := pem.Decode(decodedData)
   435  	Expect(rest).To(BeEmpty())
   436  	return pemDecodedBlock
   437  }
   438  
   439  func base64Decode(encoded string) string {
   440  	decoded, err := base64.StdEncoding.DecodeString(encoded)
   441  	Expect(err).NotTo(HaveOccurred())
   442  	return string(decoded)
   443  }
   444  
   445  func writeToFile(base64Data string) string {
   446  	file, err := ioutil.TempFile("", "")
   447  	Expect(err).NotTo(HaveOccurred())
   448  	file.WriteString(base64Decode(base64Data))
   449  	Expect(file.Close()).To(Succeed())
   450  	return file.Name()
   451  }
   452  
   453  func execCredential(status *clientauthenticationv1beta1.ExecCredentialStatus) string {
   454  	execCred, err := json.Marshal(clientauthenticationv1beta1.ExecCredential{
   455  		TypeMeta: metav1.TypeMeta{
   456  			APIVersion: "client.authentication.k8s.io/v1beta1",
   457  		},
   458  		Status: status,
   459  	})
   460  	Expect(err).NotTo(HaveOccurred())
   461  	return string(execCred)
   462  }