github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/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  	"github.com/SermoDigital/jose/crypto"
    21  	"github.com/SermoDigital/jose/jws"
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
    26  	"k8s.io/client-go/tools/clientcmd/api"
    27  )
    28  
    29  const (
    30  	clientCertData = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJVk9iMUFIckxNUjh3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRFd01EVXhOVEExTURsYUZ3MHlNakV3TURVeE5UQTFNVEZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXJrdWxLbS9qTTJhZWZsdjkKK00zQk9Jc2QvVXZrRTBONGhWb3hSeWRBbE0xQXhWd3REYUdzL3dmUzRzb0xuNHJENTF3UE1SRlNJaitwSzdGYQprRGdaR0x4UFhrai96UkZOTzcvU3J2RHYwVGxjYjJENzNCS21qaXArQ2hBWkpQdWhMQlY2VnlTN0pXSWhOM1lOCktyamR5TnB5MHN3SjI1TW9CbW1saUpFc3V2dCtDaEhseERqWE9KenF1U2owa1hPQVVsWUFTN1dKK09JMU9HbzQKUjcvdHdHZlFTNW9oYXpRVVlDR2lZSllYcjVRNkVKTmJOVVI0RjdpRSthY1I5Rm9GNnNKSmkrQStET1VDUFFSKwptbjQ5Zm1pcFVHSGtMc3BicTNFZ0FEME40VW5jcmIyeUJEMFNVTmdLQmJjclY1S2hybFA2SzkwNkY5NEpubzNHCm1Id1JwUUlEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JUV2VNZ1ZBRkRhbWcraDRqS3hoRUh2Q1l5egp5akFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUUxMWWFXQTRva1M2b3ZEWjQ1Z28ybkVZdUR4MklmRXZwYnh3CkNmYkFTNDY4M3lLT3FiYVBHOEpTVGhSbkh3TWlMcVBrbGFsdkJvV2R3aFB5Vkk0d2tVNHI4Y2c0UEpxNEZwWnQKVkNUQzFPZWVwRGpTMFpVQjRwSDVIZVlNQUxqSDBqcFV3RU96djFpaEtid05IMHFoZ2pGeUNTdld5TG9oZHdzbApJWXIvV1NEZm50NlBETC84TjFpcEJJbEN5Z1JHVGdoSFhPemhHUklPWG4rYWVOR29yWm9YWm0xbHErc1hyUnc5CktNdVZhRmdhaWVjSm0vbytyemFFSG9VZjRYOERKeVNubmVTa3ViaEx6ZERNc2o5eEs1cEJpdFgvaDlQMUQrMkcKeW5rcWdJVTJSWTM0SjBRcnU4Z0syNlJVT2pOcHIvRWJHQ0dUQUxiMXJnSDM0K2NFdlE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg=="
    31  	clientKeyData  = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ3VTNlVxYitNelpwNSsKVy8zNHpjRTRpeDM5UytRVFEzaUZXakZISjBDVXpVREZYQzBOb2F6L0I5TGl5Z3VmaXNQblhBOHhFVklpUDZrcgpzVnFRT0JrWXZFOWVTUC9ORVUwN3Y5S3U4Ty9ST1Z4dllQdmNFcWFPS240S0VCa2srNkVzRlhwWEpMc2xZaUUzCmRnMHF1TjNJMm5MU3pBbmJreWdHYWFXSWtTeTYrMzRLRWVYRU9OYzRuT3E1S1BTUmM0QlNWZ0JMdFluNDRqVTQKYWpoSHYrM0FaOUJMbWlGck5CUmdJYUpnbGhldmxEb1FrMXMxUkhnWHVJVDVweEgwV2dYcXdrbUw0RDRNNVFJOQpCSDZhZmoxK2FLbFFZZVF1eWx1cmNTQUFQUTNoU2R5dHZiSUVQUkpRMkFvRnR5dFhrcUd1VS9vcjNUb1gzZ21lCmpjYVlmQkdsQWdNQkFBRUNnZ0VBZG80WndLM3VteTM0TFBjaDM3VUU4eE1keVFkd0VmSlk3a3dWTE5MMFNNTDgKaGNKWEd1aVlKYmtLcHh6TG55L2laV0xuS25jZnFSQW9ZQUg1R2hRdWJmYlkvY2NseURVMmxhZTdCU2Y1MkJUdQpYUXhaQks3aS85ekRjdERVYWFXSFVkY2lLbGhmdStQdHVDM2ljdWJnWlJqQjljUzRCOVVtNm9XK0JSREtuandICkduQ0lEZlNNQWt4VXdTaUwwa2NXelNpZ1BYMVN3UHcxOEZvZWgzTmJEd1VXTHhxUWZLVThydVlSTUsxYUg5M3cKcjFtbjlDWUwvd0hiVWRqcmtZMlIxTjVUR21ab2Vldm5qUXgyQVc2NkYzdEg1cGg4RTF6TEFQVTl4TFdRTW9KcwpXM0gzSTdUaEYvRnJuNERQa3hQbThUUVVhQUdvQ09SSWFUQkN5VlgxQVFLQmdRREkxbkRmNWYySHdHaldrTStpCk9YbGE1R1VnRUtXaGZpeGhidE5OclNpMDU5VnZQUEJwNXdtbGQzMHJKUDhWem8vbnFnUW5ISmpmaEQ2Y3NSMTQKL2VlMHZ1Um0zYzZwZzMrODdwOHhWY3lLNHhDd0JmdFFuMGRZWWFLMkRMOEtYb0liYThpN09EQmFoNW5OZWQwcgpKa1RPcE5NRGRkL0p0bEpPZ25jRXBlUk9oUUtCZ1FEZUt1L0R1MXU1QVR3Y3p5STRXOWV1L1YwTXRwMHdqM3RpClF2MmpObW83QU1zS3BwK0ZKVDFqWFhUKzZCTm02OWpxUVJwdlAyd2RhVUdqV1dLa1lHVEVpbUZCc2ZuKzJDOFAKOEc3Uk50YWpRdEV2QlR1ZDZPN0tZUkFoTU56dm9RcDkrZmJKY1ZsRG13Nlk0bUYzUTJXS3NmZU51TGtpY3VqNQpYVFV1ekVMd29RS0JnUUREU0IvQTFYVEx4cjhwd3V6aHBGam5sQ1R3Skwrb1kzTHIya01EeUZkSWNCUU1jWWlpCnNNK2tZS2NJaUpTdnM0WWhrQ014bEpEZzVVbXNPbHVhQmVpQ3l3cHpLMEdEZWlWK285ZU90UXFLRVhkc2NLU0oKSkJiUFRVQlZHOWUyVVdiWkd0aTNrazhSOThBSkYzR0NQMWV3Um53WFpVb1FiSU5qYTJBbTJOZEJzUUtCZ0Q4eApOVXVXTWl1NE56SDJsTVExRTI4NXI4cmE4bkVLanN6UFF6ZTJWWmI4emNQMHl2RGpPOGZVb0YrVkFWZklBOFgxCnlLQVdDUm1BZytRRG03UW5tdUh3Zm1OaVRUcDRvVUpHWUM3d0N6TWE0VWNmbE9xQWc5TmFzbXpPYWpsYXRCSkwKRkRBT0pwYTlOdlN6aDRlVnl2OGRTYzJzMmpQN1BWc1ljUFVqc25LaEFvR0JBSy9kQjlnVEFpME5nczVmaVNtWQovWkp3Yk52MjcyTHdKbWV4Vit2eWtjN3J5LzRraTRQb2xRd1BHNzQ5eFZ0T2NNc2FhRlVNMVVkclN2NlIwbjlkCmpTbXhCeTl2YWdzc1FmVDNSc3BvUUJKM0w5YWxiNHM2V2ZtUEpzNkFrQkhIZHNpVXFaaElYT2J2WE1lQ0k2aVMKOTQ2R0toekFxMlVGbjhFUGxXaFVNeEFiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
    32  )
    33  
    34  var _ = Describe("KubernetesAuthentication", func() {
    35  	var (
    36  		k8sAuthWrapper    *wrapper.KubernetesAuthentication
    37  		config            *commandfakes.FakeConfig
    38  		k8sConfigGetter   *v7actionfakes.FakeKubernetesConfigGetter
    39  		wrappedConnection *ccv3fakes.FakeConnectionWrapper
    40  		req               *cloudcontroller.Request
    41  		resp              *cloudcontroller.Response
    42  		kubeConfig        *api.Config
    43  		makeErr           error
    44  	)
    45  
    46  	BeforeEach(func() {
    47  		kubeConfig = &api.Config{
    48  			Kind:       "Config",
    49  			APIVersion: "v1",
    50  			Clusters: map[string]*api.Cluster{
    51  				"my-cluster": {
    52  					Server: "https://example.org",
    53  				},
    54  			},
    55  			Contexts: map[string]*api.Context{
    56  				"my-context": {
    57  					Cluster:   "my-cluster",
    58  					AuthInfo:  "my-auth-info",
    59  					Namespace: "my-namespace",
    60  				},
    61  			},
    62  			CurrentContext: "my-context",
    63  			AuthInfos:      map[string]*api.AuthInfo{},
    64  		}
    65  
    66  		k8sConfigGetter = new(v7actionfakes.FakeKubernetesConfigGetter)
    67  		k8sConfigGetter.GetReturns(kubeConfig, nil)
    68  
    69  		config = new(commandfakes.FakeConfig)
    70  		config.CurrentUserNameReturns("auth-test", nil)
    71  
    72  		wrappedConnection = new(ccv3fakes.FakeConnectionWrapper)
    73  
    74  		httpReq, err := http.NewRequest(http.MethodPost, "", strings.NewReader("hello"))
    75  		Expect(err).NotTo(HaveOccurred())
    76  		req = cloudcontroller.NewRequest(httpReq, nil)
    77  
    78  		resp = &cloudcontroller.Response{
    79  			HTTPResponse: &http.Response{
    80  				StatusCode: http.StatusTeapot,
    81  			},
    82  		}
    83  	})
    84  
    85  	JustBeforeEach(func() {
    86  		k8sAuthWrapper = wrapper.NewKubernetesAuthentication(config, k8sConfigGetter)
    87  		k8sAuthWrapper.Wrap(wrappedConnection)
    88  
    89  		makeErr = k8sAuthWrapper.Make(req, resp)
    90  	})
    91  
    92  	When("getting the k8s config fails", func() {
    93  		BeforeEach(func() {
    94  			k8sConfigGetter.GetReturns(nil, errors.New("boom!"))
    95  		})
    96  
    97  		It("returns the error", func() {
    98  			Expect(makeErr).To(MatchError("boom!"))
    99  		})
   100  	})
   101  
   102  	When("no user is set in the config", func() {
   103  		BeforeEach(func() {
   104  			config.CurrentUserNameReturns("", nil)
   105  		})
   106  
   107  		It("errors", func() {
   108  			Expect(makeErr).To(MatchError(ContainSubstring("current user not set")))
   109  		})
   110  	})
   111  
   112  	When("there is an error getting the current user from the config", func() {
   113  		BeforeEach(func() {
   114  			config.CurrentUserNameReturns("", errors.New("boom"))
   115  		})
   116  
   117  		It("errors", func() {
   118  			Expect(makeErr).To(MatchError(ContainSubstring("boom")))
   119  		})
   120  	})
   121  
   122  	When("the chosen kubernetes auth info is not present in kubeconfig", func() {
   123  		BeforeEach(func() {
   124  			config.CurrentUserNameReturns("not-present", nil)
   125  		})
   126  
   127  		It("errors", func() {
   128  			Expect(makeErr).To(MatchError(ContainSubstring(`auth info "not-present" does not exist`)))
   129  		})
   130  	})
   131  
   132  	checkCalls := func() *cloudcontroller.Request {
   133  		Expect(makeErr).NotTo(HaveOccurred())
   134  		Expect(wrappedConnection.MakeCallCount()).To(Equal(1))
   135  
   136  		actualReq, actualResp := wrappedConnection.MakeArgsForCall(0)
   137  		Expect(actualResp.HTTPResponse).To(HaveHTTPStatus(http.StatusTeapot))
   138  
   139  		body, err := ioutil.ReadAll(actualReq.Body)
   140  		Expect(err).NotTo(HaveOccurred())
   141  		Expect(string(body)).To(Equal("hello"))
   142  
   143  		return actualReq
   144  	}
   145  
   146  	checkBearerTokenInAuthHeader := func() {
   147  		actualReq := checkCalls()
   148  
   149  		token, err := jws.ParseJWTFromRequest(actualReq.Request)
   150  		Expect(err).NotTo(HaveOccurred())
   151  		Expect(token.Validate(keyPair.Public(), crypto.SigningMethodRS256)).To(Succeed())
   152  
   153  		claims := token.Claims()
   154  		Expect(claims).To(HaveKeyWithValue("another", "thing"))
   155  	}
   156  
   157  	checkClientCertInAuthHeader := func() {
   158  		actualReq := checkCalls()
   159  
   160  		Expect(actualReq.Header).To(HaveKeyWithValue("Authorization", ConsistOf(HavePrefix("ClientCert "))))
   161  
   162  		certAndKeyPEMBase64 := actualReq.Header.Get("Authorization")[11:]
   163  		certAndKeyPEM, err := base64.StdEncoding.DecodeString(certAndKeyPEMBase64)
   164  		Expect(err).NotTo(HaveOccurred())
   165  
   166  		cert, rest := pem.Decode(certAndKeyPEM)
   167  		Expect(cert.Type).To(Equal(pemDecodeKubeConfigCertData(clientCertData).Type))
   168  		Expect(cert.Bytes).To(Equal(pemDecodeKubeConfigCertData(clientCertData).Bytes))
   169  
   170  		var key *pem.Block
   171  		key, rest = pem.Decode(rest)
   172  		Expect(key.Bytes).To(Equal(pemDecodeKubeConfigCertData(clientKeyData).Bytes))
   173  
   174  		Expect(rest).To(BeEmpty())
   175  	}
   176  
   177  	Describe("auth-provider", func() {
   178  		var token []byte
   179  
   180  		BeforeEach(func() {
   181  			jwt := jws.NewJWT(jws.Claims{
   182  				"exp":     time.Now().Add(time.Hour).Unix(),
   183  				"another": "thing",
   184  			}, crypto.SigningMethodRS256)
   185  			var err error
   186  			token, err = jwt.Serialize(keyPair)
   187  			Expect(err).NotTo(HaveOccurred())
   188  
   189  			kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   190  				AuthProvider: &api.AuthProviderConfig{
   191  					Name: "oidc",
   192  					Config: map[string]string{
   193  						"id-token":       string(token),
   194  						"idp-issuer-url": "-",
   195  						"client-id":      "-",
   196  					},
   197  				},
   198  			}
   199  		})
   200  
   201  		It("uses the auth-provider to generate the Bearer token", func() {
   202  			checkBearerTokenInAuthHeader()
   203  		})
   204  	})
   205  
   206  	Describe("client certs", func() {
   207  		var (
   208  			certFilePath string
   209  			keyFilePath  string
   210  		)
   211  
   212  		BeforeEach(func() {
   213  			certFilePath = writeToFile(clientCertData)
   214  			keyFilePath = writeToFile(clientKeyData)
   215  		})
   216  
   217  		AfterEach(func() {
   218  			Expect(os.RemoveAll(certFilePath)).To(Succeed())
   219  			Expect(os.RemoveAll(keyFilePath)).To(Succeed())
   220  		})
   221  
   222  		When("inline cert and key are provided", func() {
   223  			BeforeEach(func() {
   224  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   225  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   226  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   227  				}
   228  			})
   229  
   230  			It("puts concatenated client ceritificate and key data into the Authorization header", func() {
   231  				checkClientCertInAuthHeader()
   232  			})
   233  		})
   234  
   235  		When("cert and key are provided in files", func() {
   236  			BeforeEach(func() {
   237  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   238  					ClientCertificate: certFilePath,
   239  					ClientKey:         keyFilePath,
   240  				}
   241  			})
   242  
   243  			It("puts concatenated client ceritificate and key data into the Authorization header", func() {
   244  				checkClientCertInAuthHeader()
   245  			})
   246  
   247  			When("cert file cannot be read", func() {
   248  				BeforeEach(func() {
   249  					Expect(os.Remove(certFilePath)).To(Succeed())
   250  				})
   251  
   252  				It("returns an error", func() {
   253  					Expect(makeErr).To(MatchError(ContainSubstring(certFilePath)))
   254  				})
   255  			})
   256  
   257  			When("key file cannot be read", func() {
   258  				BeforeEach(func() {
   259  					Expect(os.Remove(keyFilePath)).To(Succeed())
   260  				})
   261  
   262  				It("returns an error", func() {
   263  					Expect(makeErr).To(MatchError(ContainSubstring(keyFilePath)))
   264  				})
   265  			})
   266  		})
   267  
   268  		When("file and inline cert is provided", func() {
   269  			BeforeEach(func() {
   270  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   271  					ClientCertificate:     certFilePath,
   272  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   273  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   274  				}
   275  			})
   276  
   277  			It("complains about invalid configuration", func() {
   278  				Expect(makeErr).To(MatchError(ContainSubstring("client-cert-data and client-cert are both specified")))
   279  			})
   280  		})
   281  
   282  		When("file and inline key is provided", func() {
   283  			BeforeEach(func() {
   284  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   285  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   286  					ClientKeyData:         []byte(base64Decode(clientKeyData)),
   287  					ClientKey:             keyFilePath,
   288  				}
   289  			})
   290  
   291  			It("complains about invalid configuration", func() {
   292  				Expect(makeErr).To(MatchError(ContainSubstring("client-key-data and client-key are both specified")))
   293  			})
   294  		})
   295  
   296  		When("inline cert and key file are provided", func() {
   297  			BeforeEach(func() {
   298  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   299  					ClientCertificateData: []byte(base64Decode(clientCertData)),
   300  					ClientKey:             keyFilePath,
   301  				}
   302  			})
   303  
   304  			It("uses the inline key", func() {
   305  				checkClientCertInAuthHeader()
   306  			})
   307  		})
   308  
   309  		When("cert file and inline key are provided", func() {
   310  			BeforeEach(func() {
   311  				kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   312  					ClientCertificate: certFilePath,
   313  					ClientKeyData:     []byte(base64Decode(clientKeyData)),
   314  				}
   315  			})
   316  
   317  			It("uses the inline key", func() {
   318  				checkClientCertInAuthHeader()
   319  			})
   320  		})
   321  	})
   322  
   323  	Describe("exec", func() {
   324  		BeforeEach(func() {
   325  			kubeConfig.AuthInfos["auth-test"] = &api.AuthInfo{
   326  				Exec: &api.ExecConfig{
   327  					APIVersion:      "client.authentication.k8s.io/v1beta1",
   328  					InteractiveMode: "Never",
   329  					Command:         "echo",
   330  				},
   331  			}
   332  		})
   333  
   334  		When("the command returns a token", func() {
   335  			BeforeEach(func() {
   336  				kubeConfig.AuthInfos["auth-test"].Exec.Args = []string{execCredential(&clientauthenticationv1beta1.ExecCredentialStatus{
   337  					Token: "a-token",
   338  				})}
   339  			})
   340  
   341  			It("uses the exec command to generate the Bearer token", func() {
   342   				helpers.SkipIfWindows()
   343  
   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  }