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 })