github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/fly/rc/target_test.go (about)

     1  package rc_test
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"github.com/pf-qiu/concourse/v6/fly/rc"
    13  	fakes "github.com/pf-qiu/concourse/v6/go-concourse/concourse/concoursefakes"
    14  	"golang.org/x/oauth2"
    15  	"sigs.k8s.io/yaml"
    16  
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  var _ = Describe("Target", func() {
    22  	const rootCA = `-----BEGIN CERTIFICATE-----
    23  MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
    24  A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
    25  cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
    26  MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
    27  BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
    28  YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
    29  ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
    30  BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
    31  I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
    32  CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
    33  lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
    34  AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
    35  -----END CERTIFICATE-----
    36  `
    37  
    38  	const clientCert = `-----BEGIN CERTIFICATE-----
    39  MIIDZTCCAk2gAwIBAgIUX7Uw88QRy27mQJqKLoCypKgEwyQwDQYJKoZIhvcNAQEL
    40  BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
    41  CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yMDA4MjYxMzQyMzVaFw0yMDA5MjUx
    42  MzQyMzVaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa
    43  BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
    44  DwAwggEKAoIBAQCnN888R5+LZQ2ngGDpcNuFLwxgIewZCSJizWlXDfGNgQZ7g/rX
    45  jsTDfsax9t5cFafvh3fC2eR5GCddNIggsA8DFEwss7e1b0bljDesqtRlXLofH0Q6
    46  Crgj9gbzorsRqLjbLNpuKD7qTeuyaaI/ofRn4blu6hx8T2r0DRgNRlTcSz/0GByK
    47  xg99aH4BmTSM6VRtkxOoXnRmTbe8EJeeva7nNAqIpQzGTnhOvEOnQsfM1wpgASS3
    48  dvQxgZi9vFPqg/bbdSYzBm1SctOXpHHERR5pgZCxP2vV6ZSxMH9cyw6jiYmSXzCq
    49  6GCRyP3ofIZi0oI/Csa10Z1l26v0ZLOJdGKtAgMBAAGjUzBRMB0GA1UdDgQWBBQV
    50  UGnNB+GFX6Rs3eKrWjIHSgEcFTAfBgNVHSMEGDAWgBQVUGnNB+GFX6Rs3eKrWjIH
    51  SgEcFTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBGqpQ+8FX5
    52  uCYd7PPeD1WNoKlIeA2FfX+SJcCFJY4MdqtjBFPTtUCMD3medXezujjwQeVR8Cup
    53  HK+3O/YEG/bNayqA8nZBF2tNGeNHXNl6iSYV1UjKvELUvh5S5QRrvrbNTmUJuSsL
    54  WHhU/KEi/FYPeOzQEGxR7meRJWggBM57b9s81W+I+XrJ71wfaYiI70GbKeRoHWA0
    55  gnhXZV7MBiEgNSHOGaavjkF22E2At40XHWL87dUJZVMp4SEu/e4XJeWvum21VDrs
    56  mlR/qNacLCRV2kECu+9YEtT//lb5njdHkMMeXmYp4lrNpCuXzbAWpRo8VER6RQ2Q
    57  6vNmvRoBV5G1
    58  -----END CERTIFICATE-----
    59  `
    60  
    61  	const clientKey = `-----BEGIN PRIVATE KEY-----
    62  MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnN888R5+LZQ2n
    63  gGDpcNuFLwxgIewZCSJizWlXDfGNgQZ7g/rXjsTDfsax9t5cFafvh3fC2eR5GCdd
    64  NIggsA8DFEwss7e1b0bljDesqtRlXLofH0Q6Crgj9gbzorsRqLjbLNpuKD7qTeuy
    65  aaI/ofRn4blu6hx8T2r0DRgNRlTcSz/0GByKxg99aH4BmTSM6VRtkxOoXnRmTbe8
    66  EJeeva7nNAqIpQzGTnhOvEOnQsfM1wpgASS3dvQxgZi9vFPqg/bbdSYzBm1SctOX
    67  pHHERR5pgZCxP2vV6ZSxMH9cyw6jiYmSXzCq6GCRyP3ofIZi0oI/Csa10Z1l26v0
    68  ZLOJdGKtAgMBAAECggEABX72Frsb6U729exoQwPskyIKvBYhVmlQcgLiVXQl3krB
    69  VcnusqsEmJBQI4VDpa8oh9zh+MuEkN5UXOHfH4Pp2mYOYuG9Rf9USzMimVA8DuDP
    70  VTqH2YiEqNnrPJK6p0fuW3XL8BbuinDpMEH8jS7bg5aNq7GSIhvSHhdYFQecvmjF
    71  JRhQCcp/kvy7YgCG+Eg9bNYyGTKjx7WEWxY0BtF4saqiGN3l8T4gGHMdk/RBCkPZ
    72  QG69V0FyC5iAi07J/x9eNSIwuq4egzzdraklnaLcrsSEOvjvTDl9+xUEESFW/OTO
    73  b6RD5uGahhjJoX+WCOX01E7EN2EP1QgU+Fookq4YWQKBgQDSbIyRjQokH/DnNon/
    74  Fn8/cChz1vPgMDHojRP/pxk/gMC8Jcp0JrpKFMZcPphkuc5O6rjf8BExUncESXec
    75  uAcr9+G/TS1fA8eRpgFS3gmSwmQsJHRiNprhyGKM6WOhIa1Oi5ku6ZGZZf/GGaEA
    76  Fv2jqV1RanVxwU3mNW8rP7V/6wKBgQDLb5olPo/IfBf9oP9nwhiWK1ZPErICdiH7
    77  UtADDCTZhAE9VNSOvf78PduEKXVDKUWj5IaSgaijIj6edcYbCmRjqzG5g1choyAY
    78  hZ/hAUX3X6nA1lc/ND/v7epVjZ8I7vzf72VRfDpTNdnjoRMzhR4c1HpKfdZwzTXT
    79  AWFWRaAZxwKBgGmUY3eIb+UuTZ6Fg/oE3LYE3Zc57EW5iOEpIDavLgDp5krBH3Lm
    80  F6SiBeE02xv3CqgYJ8jc2JOJ0APLpQNyZs7N4mwtGi3JZLIUvCdLFzyW4tIvPGIn
    81  CdFtzNztIbswfZeifarHMPHp9sr8AwdbgcpDaXo3U1RPbHmsp+noXnYfAoGBAMp9
    82  OyD3NIaJfheluJK+T1qpqC7snOJ2UzylIQbnf4ZCLjmtxiSOWM8ZgvX5jg5bdkW7
    83  oXcSN5io7UssTxN7NJFARS4x3PhONhQybQC5E7s2LPEUZ6MxjrJyTVz6qeFqf6kl
    84  z+Nbk3Jfl5FLMqGFToPDujWLK3b7yydLqGcGxmThAoGBALrluhB3186MZsZGPccc
    85  gEyHgI3smYf7BBSlkOOj3Ws8tckiVv0WXKmk8kpB2H4Z49l1vkqT0fqLdZGioUCT
    86  pOyP4gMpJpf2MofQq96Ng4GFSoSl7i52olbd7e6P6IGP01AAwwlmF64dl5oGk4hW
    87  Lfkzl8ebb+tt0XFMUFc42WNr
    88  -----END PRIVATE KEY-----
    89  `
    90  
    91  	var (
    92  		tmpDir string
    93  		flyrc  string
    94  	)
    95  
    96  	BeforeEach(func() {
    97  		var err error
    98  		tmpDir, err = ioutil.TempDir("", "fly-test")
    99  		Expect(err).ToNot(HaveOccurred())
   100  
   101  		os.Setenv("HOME", tmpDir)
   102  
   103  		flyrc = filepath.Join(userHomeDir(), ".flyrc")
   104  	})
   105  
   106  	AfterEach(func() {
   107  		os.RemoveAll(tmpDir)
   108  	})
   109  
   110  	Describe("Complete", func() {
   111  		BeforeEach(func() {
   112  			flyrcContents := `targets:
   113    some-target-b: {}
   114    some-target-a: {}
   115    another-target: {}
   116    `
   117  			ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   118  		})
   119  
   120  		AfterEach(func() {
   121  			os.RemoveAll(tmpDir)
   122  		})
   123  
   124  		It("lists matching targets in order", func() {
   125  			name := rc.TargetName("some-target")
   126  			comps := name.Complete("some-target")
   127  			Expect(comps).To(HaveLen(2))
   128  			Expect(comps[0].Item).To(Equal("some-target-a"))
   129  			Expect(comps[1].Item).To(Equal("some-target-b"))
   130  		})
   131  	})
   132  
   133  	Describe("LoadTarget", func() {
   134  		Context("when there is no ca-cert", func() {
   135  			BeforeEach(func() {
   136  				flyrcContents := `targets:
   137    some-target:
   138      api: http://concourse.com
   139      insecure: true
   140      token:
   141        type: Bearer
   142        value: some-token`
   143  				ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   144  			})
   145  
   146  			It("loads target with correct transport", func() {
   147  				target, err := rc.LoadTarget("some-target", false)
   148  				Expect(err).NotTo(HaveOccurred())
   149  				transport, ok := target.Client().HTTPClient().Transport.(*oauth2.Transport)
   150  				Expect(ok).To(BeTrue())
   151  				Expect((*transport).Source).To(Equal(oauth2.StaticTokenSource(&oauth2.Token{
   152  					TokenType:   "Bearer",
   153  					AccessToken: "some-token",
   154  				})))
   155  				base, ok := (*transport).Base.(*http.Transport)
   156  				Expect(ok).To(BeTrue())
   157  				Expect((*base).TLSClientConfig).To(Equal(&tls.Config{
   158  					InsecureSkipVerify: true,
   159  					RootCAs:            nil,
   160  					Certificates:       []tls.Certificate{},
   161  				}))
   162  			})
   163  		})
   164  
   165  		Context("when there is ca-cert", func() {
   166  			BeforeEach(func() {
   167  				flyrcConfig := rc.RC{
   168  					Targets: map[rc.TargetName]rc.TargetProps{
   169  						"some-target": {
   170  							API:      "http://concourse.com",
   171  							CACert:   rootCA,
   172  							TeamName: "some-team",
   173  							Token: &rc.TargetToken{
   174  								Type:  "Bearer",
   175  								Value: "some-token",
   176  							},
   177  						},
   178  					},
   179  				}
   180  				flyrcContents, err := yaml.Marshal(flyrcConfig)
   181  				Expect(err).NotTo(HaveOccurred())
   182  
   183  				ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   184  			})
   185  
   186  			It("loads target with correct transport", func() {
   187  				target, err := rc.LoadTarget("some-target", false)
   188  				Expect(err).NotTo(HaveOccurred())
   189  				transport, ok := target.Client().HTTPClient().Transport.(*oauth2.Transport)
   190  				Expect(ok).To(BeTrue())
   191  				base, ok := (*transport).Base.(*http.Transport)
   192  				Expect(ok).To(BeTrue())
   193  
   194  				var expectedCaCertPool *x509.CertPool
   195  				if runtime.GOOS != "windows" {
   196  					expectedCaCertPool, err = x509.SystemCertPool()
   197  					Expect(err).NotTo(HaveOccurred())
   198  				} else {
   199  					expectedCaCertPool = x509.NewCertPool()
   200  				}
   201  				ok = expectedCaCertPool.AppendCertsFromPEM([]byte(rootCA))
   202  				Expect(ok).To(BeTrue())
   203  
   204  				Expect((*base).TLSClientConfig).To(Equal(&tls.Config{
   205  					InsecureSkipVerify: false,
   206  					RootCAs:            expectedCaCertPool,
   207  					Certificates:       []tls.Certificate{},
   208  				}))
   209  			})
   210  		})
   211  
   212  		Context("when there is a client certificate path and a client key path", func() {
   213  			BeforeEach(func() {
   214  				certPath := filepath.Join(userHomeDir(), "client.pem")
   215  				keyPath := filepath.Join(userHomeDir(), "client.key")
   216  
   217  				err := ioutil.WriteFile(certPath, []byte(clientCert), 0600)
   218  
   219  				Expect(err).ToNot(HaveOccurred())
   220  
   221  				err = ioutil.WriteFile(keyPath, []byte(clientKey), 0600)
   222  				Expect(err).ToNot(HaveOccurred())
   223  
   224  				flyrcConfig := rc.RC{
   225  					Targets: map[rc.TargetName]rc.TargetProps{
   226  						"some-target": {
   227  							API:            "http://concourse.com",
   228  							ClientCertPath: certPath,
   229  							ClientKeyPath:  keyPath,
   230  							TeamName:       "some-team",
   231  							Token: &rc.TargetToken{
   232  								Type:  "Bearer",
   233  								Value: "some-token",
   234  							},
   235  						},
   236  					},
   237  				}
   238  				flyrcContents, err := yaml.Marshal(flyrcConfig)
   239  				Expect(err).NotTo(HaveOccurred())
   240  
   241  				ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   242  			})
   243  
   244  			It("loads target with correct transport", func() {
   245  				target, err := rc.LoadTarget("some-target", false)
   246  				Expect(err).NotTo(HaveOccurred())
   247  				transport, ok := target.Client().HTTPClient().Transport.(*oauth2.Transport)
   248  				Expect(ok).To(BeTrue())
   249  				base, ok := (*transport).Base.(*http.Transport)
   250  				Expect(ok).To(BeTrue())
   251  
   252  				expectedX509Cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
   253  
   254  				Expect((*base).TLSClientConfig).To(Equal(&tls.Config{
   255  					InsecureSkipVerify: false,
   256  					Certificates:       []tls.Certificate{expectedX509Cert},
   257  				}))
   258  			})
   259  		})
   260  
   261  		Context("when there is a client certificate path, but no client key path", func() {
   262  			BeforeEach(func() {
   263  				certPath := filepath.Join(userHomeDir(), "client.pem")
   264  
   265  				err := ioutil.WriteFile(certPath, []byte(clientCert), 0600)
   266  				Expect(err).ToNot(HaveOccurred())
   267  
   268  				flyrcConfig := rc.RC{
   269  					Targets: map[rc.TargetName]rc.TargetProps{
   270  						"some-target": {
   271  							API:            "http://concourse.com",
   272  							ClientCertPath: certPath,
   273  							TeamName:       "some-team",
   274  							Token: &rc.TargetToken{
   275  								Type:  "Bearer",
   276  								Value: "some-token",
   277  							},
   278  						},
   279  					},
   280  				}
   281  				flyrcContents, err := yaml.Marshal(flyrcConfig)
   282  				Expect(err).NotTo(HaveOccurred())
   283  
   284  				ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   285  			})
   286  
   287  			It("warns the user and exits with failure", func() {
   288  				_, err := rc.LoadTarget("some-target", false)
   289  				Expect(err).Should(MatchError("A client certificate may not be declared without defining a client key"))
   290  			})
   291  		})
   292  
   293  		Context("when there is a client key path, but no client certificate path", func() {
   294  			BeforeEach(func() {
   295  				keyPath := filepath.Join(userHomeDir(), "client.key")
   296  
   297  				err := ioutil.WriteFile(keyPath, []byte(clientKey), 0600)
   298  				Expect(err).ToNot(HaveOccurred())
   299  
   300  				flyrcConfig := rc.RC{
   301  					Targets: map[rc.TargetName]rc.TargetProps{
   302  						"some-target": {
   303  							API:           "http://concourse.com",
   304  							ClientKeyPath: keyPath,
   305  							TeamName:      "some-team",
   306  							Token: &rc.TargetToken{
   307  								Type:  "Bearer",
   308  								Value: "some-token",
   309  							},
   310  						},
   311  					},
   312  				}
   313  				flyrcContents, err := yaml.Marshal(flyrcConfig)
   314  				Expect(err).NotTo(HaveOccurred())
   315  
   316  				ioutil.WriteFile(flyrc, []byte(flyrcContents), 0777)
   317  			})
   318  
   319  			It("warns the user and exits with failure", func() {
   320  				_, err := rc.LoadTarget("some-target", false)
   321  				Expect(err).Should(MatchError("A client key may not be declared without defining a client certificate"))
   322  			})
   323  		})
   324  	})
   325  
   326  	Describe("FindTeam", func() {
   327  		It("finds the team", func() {
   328  			fakeClient := new(fakes.FakeClient)
   329  
   330  			rc.NewTarget(
   331  				"test-target",
   332  				"default-team",
   333  				"http://example.com",
   334  				nil,
   335  				"ca-cert",
   336  				nil,
   337  				"",
   338  				"",
   339  				[]tls.Certificate{},
   340  				true,
   341  				fakeClient,
   342  			).FindTeam("the-team")
   343  
   344  			Expect(fakeClient.FindTeamCallCount()).To(Equal(1), "client.FindTeam should be used")
   345  			Expect(fakeClient.FindTeamArgsForCall(0)).To(Equal("the-team"), "FindTeam should pass through team name")
   346  		})
   347  	})
   348  })