github.com/openshift-online/ocm-sdk-go@v0.1.473/connection_test.go (about) 1 /* 2 Copyright (c) 2019 Red Hat, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This file contains tests for the connection. 18 19 package sdk 20 21 import ( 22 "context" 23 "net/http" 24 "os" 25 "path/filepath" 26 "time" 27 28 . "github.com/onsi/ginkgo/v2/dsl/core" // nolint 29 . "github.com/onsi/gomega" // nolint 30 . "github.com/onsi/gomega/gbytes" // nolint 31 32 . "github.com/openshift-online/ocm-sdk-go/testing" // nolint 33 ) 34 35 var _ = Describe("Connection", func() { 36 It("Can be created with access token", func() { 37 accessToken := MakeTokenString("Bearer", 5*time.Minute) 38 connection, err := NewConnectionBuilder(). 39 Logger(logger). 40 Tokens(accessToken). 41 Build() 42 Expect(err).ToNot(HaveOccurred()) 43 defer connection.Close() 44 Expect(connection).ToNot(BeNil()) 45 }) 46 47 It("Can be created with refresh token", func() { 48 refreshToken := MakeTokenString("Refresh", 10*time.Hour) 49 connection, err := NewConnectionBuilder(). 50 Logger(logger). 51 Tokens(refreshToken). 52 Build() 53 Expect(err).ToNot(HaveOccurred()) 54 defer connection.Close() 55 Expect(connection).ToNot(BeNil()) 56 }) 57 58 It("Can be created with offline access token", func() { 59 offlineToken := MakeTokenString("Offline", 0) 60 connection, err := NewConnectionBuilder(). 61 Logger(logger). 62 Tokens(offlineToken). 63 Build() 64 Expect(err).ToNot(HaveOccurred()) 65 defer connection.Close() 66 Expect(connection).ToNot(BeNil()) 67 }) 68 69 It("Can be created with access and refresh tokens", func() { 70 accessToken := MakeTokenString("Bearer", 5*time.Minute) 71 refreshToken := MakeTokenString("Refresh", 10*time.Hour) 72 connection, err := NewConnectionBuilder(). 73 Logger(logger). 74 Tokens(accessToken, refreshToken). 75 Build() 76 Expect(err).ToNot(HaveOccurred()) 77 defer connection.Close() 78 Expect(connection).ToNot(BeNil()) 79 }) 80 81 It("Can be created with access and offline tokens", func() { 82 accessToken := MakeTokenString("Bearer", 5*time.Minute) 83 offlineToken := MakeTokenString("Offline", 10*time.Hour) 84 connection, err := NewConnectionBuilder(). 85 Logger(logger). 86 Tokens(accessToken, offlineToken). 87 Build() 88 Expect(err).ToNot(HaveOccurred()) 89 defer connection.Close() 90 Expect(connection).ToNot(BeNil()) 91 }) 92 93 It("Can be created with user name and password", func() { 94 connection, err := NewConnectionBuilder(). 95 Logger(logger). 96 User("myuser", "mypassword"). 97 Build() 98 Expect(err).ToNot(HaveOccurred()) 99 defer connection.Close() 100 Expect(connection).ToNot(BeNil()) 101 }) 102 103 It("Can be created with client identifier and secret", func() { 104 connection, err := NewConnectionBuilder(). 105 Logger(logger). 106 Client("myclientid", "myclientsecret"). 107 Build() 108 Expect(err).ToNot(HaveOccurred()) 109 defer connection.Close() 110 Expect(connection).ToNot(BeNil()) 111 }) 112 113 It("Can be created with metrics subsystem", func() { 114 accessToken := MakeTokenString("Bearer", 5*time.Minute) 115 connection, err := NewConnectionBuilder(). 116 Logger(logger). 117 Tokens(accessToken). 118 MetricsSubsystem("my_subsystem"). 119 Build() 120 Expect(err).ToNot(HaveOccurred()) 121 Expect(connection).ToNot(BeNil()) 122 defer func() { 123 err = connection.Close() 124 Expect(err).ToNot(HaveOccurred()) 125 }() 126 Expect(connection.MetricsSubsystem()).To(Equal("my_subsystem")) 127 }) 128 129 It("Selects default OpenID server with default access token", func() { 130 accessToken := MakeTokenString("Bearer", 5*time.Minute) 131 connection, err := NewConnectionBuilder(). 132 Logger(logger). 133 Tokens(accessToken). 134 Build() 135 Expect(err).ToNot(HaveOccurred()) 136 defer connection.Close() 137 tokenURL := connection.TokenURL() 138 Expect(tokenURL).To(Equal(DefaultTokenURL)) 139 clientID, clientSecret := connection.Client() 140 Expect(clientID).To(Equal(DefaultClientID)) 141 Expect(clientSecret).To(Equal(DefaultClientSecret)) 142 }) 143 144 It("Selects default OpenID server with default refresh token", func() { 145 refreshToken := MakeTokenString("Refresh", 10*time.Hour) 146 connection, err := NewConnectionBuilder(). 147 Logger(logger). 148 Tokens(refreshToken). 149 Build() 150 Expect(err).ToNot(HaveOccurred()) 151 defer connection.Close() 152 tokenURL := connection.TokenURL() 153 Expect(tokenURL).To(Equal(DefaultTokenURL)) 154 clientID, clientSecret := connection.Client() 155 Expect(clientID).To(Equal(DefaultClientID)) 156 Expect(clientSecret).To(Equal(DefaultClientSecret)) 157 }) 158 159 It("Selects default OpenID server with default offline access token", func() { 160 offlineToken := MakeTokenString("Offline", 0) 161 connection, err := NewConnectionBuilder(). 162 Logger(logger). 163 Tokens(offlineToken). 164 Build() 165 Expect(err).ToNot(HaveOccurred()) 166 defer connection.Close() 167 tokenURL := connection.TokenURL() 168 Expect(tokenURL).To(Equal(DefaultTokenURL)) 169 clientID, clientSecret := connection.Client() 170 Expect(clientID).To(Equal(DefaultClientID)) 171 Expect(clientSecret).To(Equal(DefaultClientSecret)) 172 }) 173 174 It("Honours explicitly provided OpenID server with user name and password", func() { 175 connection, err := NewConnectionBuilder(). 176 Logger(logger). 177 User("myuser", "mypassword"). 178 TokenURL(DefaultTokenURL). 179 Client(DefaultClientID, DefaultClientSecret). 180 Build() 181 Expect(err).ToNot(HaveOccurred()) 182 defer connection.Close() 183 tokenURL := connection.TokenURL() 184 Expect(tokenURL).To(Equal(DefaultTokenURL)) 185 clientID, clientSecret := connection.Client() 186 Expect(clientID).To(Equal(DefaultClientID)) 187 Expect(clientSecret).To(Equal(DefaultClientSecret)) 188 }) 189 190 It("Use transport wrapper", func() { 191 // Create a connection: 192 transport := NewTestTransport() 193 connection, err := NewConnectionBuilder(). 194 Logger(logger). 195 User("test", "test"). 196 TransportWrapper(func(wrapped http.RoundTripper) http.RoundTripper { 197 return transport 198 }). 199 Build() 200 Expect(err).ToNot(HaveOccurred()) 201 defer connection.Close() 202 203 // Try to get the tokens using a explicit and short timeout to make the test run 204 // faster (by default it takes up to 15 seconds) but give it enough time to retry 205 // a few times: 206 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 207 defer cancel() 208 _, _, err = connection.TokensContext(ctx) 209 210 // Check that the transport was called at least three times: 211 Expect(transport.called).To(BeNumerically(">=", 3)) 212 Expect(err).To(HaveOccurred()) 213 }) 214 215 It("Can be created with one alternative URL", func() { 216 // Create the connection: 217 token := MakeTokenString("Bearer", 5*time.Minute) 218 connection, err := NewConnectionBuilder(). 219 Logger(logger). 220 Tokens(token). 221 URL("https://my.server.com"). 222 AlternativeURL("/api/clusters_mgmt", "https://your.server.com"). 223 Build() 224 Expect(err).ToNot(HaveOccurred()) 225 226 // Check that the URLs are set: 227 Expect(connection.URL()).To(Equal("https://my.server.com")) 228 alternativeURLs := connection.AlternativeURLs() 229 Expect(alternativeURLs).To(HaveLen(1)) 230 Expect(alternativeURLs).To(HaveKeyWithValue( 231 "/api/clusters_mgmt", 232 "https://your.server.com", 233 )) 234 }) 235 236 It("Can be created with two alternative URLs", func() { 237 // Create the connection: 238 token := MakeTokenString("Bearer", 5*time.Minute) 239 connection, err := NewConnectionBuilder(). 240 Logger(logger). 241 Tokens(token). 242 URL("https://my.server.com"). 243 AlternativeURL("/api/clusters_mgmt", "https://your.server.com"). 244 AlternativeURL("/api/accounts_mgmt", "https://her.server.com"). 245 Build() 246 Expect(err).ToNot(HaveOccurred()) 247 248 // Check that the URLs are set: 249 Expect(connection.URL()).To(Equal("https://my.server.com")) 250 alternativeURLs := connection.AlternativeURLs() 251 Expect(alternativeURLs).To(HaveLen(2)) 252 Expect(alternativeURLs).To(HaveKeyWithValue( 253 "/api/clusters_mgmt", 254 "https://your.server.com", 255 )) 256 Expect(alternativeURLs).To(HaveKeyWithValue( 257 "/api/accounts_mgmt", 258 "https://her.server.com", 259 )) 260 }) 261 262 It("Can be created with a map of alternative URLs", func() { 263 // Create the connection: 264 token := MakeTokenString("Bearer", 5*time.Minute) 265 connection, err := NewConnectionBuilder(). 266 Logger(logger). 267 Tokens(token). 268 URL("https://my.server.com"). 269 AlternativeURLs(map[string]string{ 270 "/api/clusters_mgmt": "https://your.server.com", 271 "/api/accounts_mgmt": "https://her.server.com", 272 }). 273 Build() 274 Expect(err).ToNot(HaveOccurred()) 275 276 // Check that the URLs are set: 277 Expect(connection.URL()).To(Equal("https://my.server.com")) 278 alternativeURLs := connection.AlternativeURLs() 279 Expect(alternativeURLs).To(HaveLen(2)) 280 Expect(alternativeURLs).To(HaveKeyWithValue( 281 "/api/clusters_mgmt", 282 "https://your.server.com", 283 )) 284 Expect(alternativeURLs).To(HaveKeyWithValue( 285 "/api/accounts_mgmt", 286 "https://her.server.com", 287 )) 288 }) 289 290 It("Altering returned alternative URLs doesn't affect internal state", func() { 291 // Create the connection: 292 token := MakeTokenString("Bearer", 5*time.Minute) 293 connection, err := NewConnectionBuilder(). 294 Logger(logger). 295 Tokens(token). 296 URL("https://my.server.com"). 297 AlternativeURLs(map[string]string{ 298 "/api/clusters_mgmt": "https://your.server.com", 299 "/api/accounts_mgmt": "https://her.server.com", 300 }). 301 Build() 302 Expect(err).ToNot(HaveOccurred()) 303 304 // Try to modify the returned map of alternative URLs: 305 alternativeURLs := connection.AlternativeURLs() 306 alternativeURLs["/api/service_logs"] = "https://his.server.com" 307 308 // Check that map used internall hasn't changed: 309 alternativeURLs = connection.AlternativeURLs() 310 Expect(alternativeURLs).To(HaveLen(2)) 311 Expect(alternativeURLs).To(HaveKeyWithValue( 312 "/api/clusters_mgmt", 313 "https://your.server.com", 314 )) 315 Expect(alternativeURLs).To(HaveKeyWithValue( 316 "/api/accounts_mgmt", 317 "https://her.server.com", 318 )) 319 }) 320 321 It("Can't be created with invalid alternative URL prefix", func() { 322 token := MakeTokenString("Bearer", 5*time.Minute) 323 _, err := NewConnectionBuilder(). 324 Logger(logger). 325 Tokens(token). 326 AlternativeURL("junk", "https://api.openshift.com"). 327 Build() 328 Expect(err).To(HaveOccurred()) 329 }) 330 331 It("Can't be created with invalid alternative URL", func() { 332 token := MakeTokenString("Bearer", 5*time.Minute) 333 _, err := NewConnectionBuilder(). 334 Logger(logger). 335 Tokens(token). 336 AlternativeURL("/api/clusters_mgmt", ":junk"). 337 Build() 338 Expect(err).To(HaveOccurred()) 339 }) 340 341 It("Can be configured with a YAML string", func() { 342 // Create temporary files for the trusted CAs: 343 tmp, err := os.MkdirTemp("", "*.test.cas") 344 Expect(err).ToNot(HaveOccurred()) 345 defer func() { 346 err = os.RemoveAll(tmp) 347 Expect(err).ToNot(HaveOccurred()) 348 }() 349 err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600) 350 Expect(err).ToNot(HaveOccurred()) 351 err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600) 352 Expect(err).ToNot(HaveOccurred()) 353 354 // Create the YAML configuration string: 355 fileAccess := MakeTokenString("Bearer", 5*time.Minute) 356 fileRefresh := MakeTokenString("Refresh", 10*time.Hour) 357 content := EvaluateTemplate( 358 ` 359 url: https://my.server.com 360 alternative_urls: 361 /api/clusters_mgmt: https://your.server.com 362 /api/accounts_mgmt: https://her.server.com 363 token_url: https://openid.server.com 364 user: myuser 365 password: mypassword 366 client_id: myclient 367 client_secret: mysecret 368 tokens: 369 - {{ .AccessToken }} 370 - {{ .RefreshToken }} 371 scopes: 372 - openid 373 - myscope 374 insecure: true 375 trusted_cas: 376 - {{ .Tmp }}/myca.pem 377 - {{ .Tmp }}/yourca.pem 378 agent: myagent 379 retry_limit: 4 380 metrics_subsystem: mysubsystem 381 `, 382 "Tmp", tmp, 383 "AccessToken", fileAccess, 384 "RefreshToken", fileRefresh, 385 ) 386 387 // Create the connection and verify it has been created with the configuration 388 // stored in the YAML string: 389 connection, err := NewConnectionBuilder(). 390 Logger(logger). 391 Load(content). 392 Build() 393 Expect(err).ToNot(HaveOccurred()) 394 Expect(connection.URL()).To(Equal("https://my.server.com")) 395 alternativeURLs := connection.AlternativeURLs() 396 Expect(alternativeURLs).To(HaveLen(2)) 397 Expect(alternativeURLs).To(HaveKeyWithValue( 398 "/api/clusters_mgmt", "https://your.server.com", 399 )) 400 Expect(alternativeURLs).To(HaveKeyWithValue( 401 "/api/accounts_mgmt", "https://her.server.com", 402 )) 403 Expect(connection.TokenURL()).To(Equal("https://openid.server.com")) 404 user, password := connection.User() 405 Expect(user).To(Equal("myuser")) 406 Expect(password).To(Equal("mypassword")) 407 client, secret := connection.Client() 408 Expect(client).To(Equal("myclient")) 409 Expect(secret).To(Equal("mysecret")) 410 returnedAccess, returnedRefresh, err := connection.Tokens() 411 Expect(err).ToNot(HaveOccurred()) 412 Expect(returnedAccess).To(Equal(fileAccess)) 413 Expect(returnedRefresh).To(Equal(fileRefresh)) 414 defer func() { 415 err = connection.Close() 416 Expect(err).ToNot(HaveOccurred()) 417 }() 418 Expect(connection.Scopes()).To(ConsistOf("openid", "myscope")) 419 Expect(connection.Insecure()).To(BeTrue()) 420 Expect(connection.Agent()).To(Equal("myagent")) 421 Expect(connection.RetryLimit()).To(Equal(4)) 422 Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem")) 423 }) 424 425 It("Can be configured with a YAML file", func() { 426 // Create temporary files for the trusted CAs: 427 tmp, err := os.MkdirTemp("", "*.test.cas") 428 Expect(err).ToNot(HaveOccurred()) 429 defer func() { 430 err = os.RemoveAll(tmp) 431 Expect(err).ToNot(HaveOccurred()) 432 }() 433 err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600) 434 Expect(err).ToNot(HaveOccurred()) 435 err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600) 436 Expect(err).ToNot(HaveOccurred()) 437 438 // Create a temporary YAML file containing the configuration: 439 fileAccess := MakeTokenString("Bearer", 5*time.Minute) 440 fileRefresh := MakeTokenString("Refresh", 10*time.Hour) 441 content := EvaluateTemplate( 442 ` 443 url: https://my.server.com 444 alternative_urls: 445 /api/clusters_mgmt: https://your.server.com 446 /api/accounts_mgmt: https://her.server.com 447 token_url: https://openid.server.com 448 user: myuser 449 password: mypassword 450 client_id: myclient 451 client_secret: mysecret 452 tokens: 453 - {{ .AccessToken }} 454 - {{ .RefreshToken }} 455 scopes: 456 - openid 457 - myscope 458 insecure: true 459 trusted_cas: 460 - {{ .Tmp }}/myca.pem 461 - {{ .Tmp }}/yourca.pem 462 agent: myagent 463 retry_limit: 4 464 metrics_subsystem: mysubsystem 465 `, 466 "Tmp", tmp, 467 "AccessToken", fileAccess, 468 "RefreshToken", fileRefresh, 469 ) 470 file, err := os.CreateTemp("", "*.yaml") 471 Expect(err).ToNot(HaveOccurred()) 472 path := file.Name() 473 defer func() { 474 err = os.Remove(path) 475 Expect(err).ToNot(HaveOccurred()) 476 }() 477 _, err = file.WriteString(content) 478 Expect(err).ToNot(HaveOccurred()) 479 err = file.Close() 480 Expect(err).ToNot(HaveOccurred()) 481 482 // Create the connection and verify it has been created with the configuration 483 // stored in the YAML file: 484 connection, err := NewConnectionBuilder(). 485 Logger(logger). 486 Load(path). 487 Build() 488 Expect(err).ToNot(HaveOccurred()) 489 Expect(connection.URL()).To(Equal("https://my.server.com")) 490 alternativeURLs := connection.AlternativeURLs() 491 Expect(alternativeURLs).To(HaveLen(2)) 492 Expect(alternativeURLs).To(HaveKeyWithValue( 493 "/api/clusters_mgmt", "https://your.server.com", 494 )) 495 Expect(alternativeURLs).To(HaveKeyWithValue( 496 "/api/accounts_mgmt", "https://her.server.com", 497 )) 498 Expect(connection.TokenURL()).To(Equal("https://openid.server.com")) 499 user, password := connection.User() 500 Expect(user).To(Equal("myuser")) 501 Expect(password).To(Equal("mypassword")) 502 client, secret := connection.Client() 503 Expect(client).To(Equal("myclient")) 504 Expect(secret).To(Equal("mysecret")) 505 returnedAccess, returnedRefresh, err := connection.Tokens() 506 Expect(err).ToNot(HaveOccurred()) 507 Expect(returnedAccess).To(Equal(fileAccess)) 508 Expect(returnedRefresh).To(Equal(fileRefresh)) 509 defer func() { 510 err = connection.Close() 511 Expect(err).ToNot(HaveOccurred()) 512 }() 513 Expect(connection.Scopes()).To(ConsistOf("openid", "myscope")) 514 Expect(connection.Insecure()).To(BeTrue()) 515 Expect(connection.Agent()).To(Equal("myagent")) 516 Expect(connection.RetryLimit()).To(Equal(4)) 517 Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem")) 518 }) 519 520 It("Method calls after load override configuration file", func() { 521 // Create temporary files for the trusted CAs: 522 tmp, err := os.MkdirTemp("", "*.test.cas") 523 Expect(err).ToNot(HaveOccurred()) 524 defer func() { 525 err = os.RemoveAll(tmp) 526 Expect(err).ToNot(HaveOccurred()) 527 }() 528 err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600) 529 Expect(err).ToNot(HaveOccurred()) 530 err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600) 531 Expect(err).ToNot(HaveOccurred()) 532 533 // Create a temporary YAML file containing the configuration: 534 fileAccess := MakeTokenString("Bearer", 5*time.Minute) 535 fileRefresh := MakeTokenString("Refresh", 10*time.Hour) 536 content := EvaluateTemplate( 537 ` 538 url: https://my.server.com 539 alternative_urls: 540 /api/clusters_mgmt: https://your.server.com 541 /api/accounts_mgmt: https://her.server.com 542 token_url: https://openid.server.com 543 user: myuser 544 password: mypassword 545 client_id: myclient 546 client_secret: mysecret 547 tokens: 548 - {{ .AccessToken }} 549 - {{ .RefreshToken }} 550 scopes: 551 - openid 552 - myscope 553 insecure: true 554 trusted_cas: 555 - {{ .Tmp }}/myca.pem 556 - {{ .Tmp }}/yourca.pem 557 agent: myagent 558 retry_limit: 5 559 metrics_subsystem: mysubsystem 560 `, 561 "Tmp", tmp, 562 "AccessToken", fileAccess, 563 "RefreshToken", fileRefresh, 564 ) 565 file, err := os.CreateTemp("", "*.yaml") 566 Expect(err).ToNot(HaveOccurred()) 567 path := file.Name() 568 defer func() { 569 err = os.Remove(path) 570 Expect(err).ToNot(HaveOccurred()) 571 }() 572 _, err = file.WriteString(content) 573 Expect(err).ToNot(HaveOccurred()) 574 err = file.Close() 575 Expect(err).ToNot(HaveOccurred()) 576 577 // Load the configuration file and then configure the connection with method 578 // calls: 579 overridenAccess := MakeTokenString("Bearer", 5*time.Minute) 580 overridenRefresh := MakeTokenString("Refresh", 10*time.Hour) 581 connection, err := NewConnectionBuilder(). 582 Logger(logger). 583 Load(path). 584 URL("https://overriden.my.server.com"). 585 AlternativeURL("/api/clusters_mgmt", "https://overriden.your.server.com"). 586 AlternativeURL("/api/accounts_mgmt", "https://overriden.her.server.com"). 587 TokenURL("https://overriden.openid.server.com"). 588 User("overriden.myuser", "overriden.mypassword"). 589 Client("overriden.myclient", "overriden.mysecret"). 590 Tokens(overridenAccess, overridenRefresh). 591 Scopes("openid", "overriden.myscope"). 592 Insecure(false). 593 Agent("overriden.myagent"). 594 RetryLimit(4). 595 MetricsSubsystem("overriden_mysubsystem"). 596 Build() 597 Expect(err).ToNot(HaveOccurred()) 598 599 // Check that the actual settings are the ones set with the method calls: 600 Expect(connection.URL()).To(Equal("https://overriden.my.server.com")) 601 alternativeURLs := connection.AlternativeURLs() 602 Expect(alternativeURLs).To(HaveLen(2)) 603 Expect(alternativeURLs).To(HaveKeyWithValue( 604 "/api/clusters_mgmt", "https://overriden.your.server.com", 605 )) 606 Expect(alternativeURLs).To(HaveKeyWithValue( 607 "/api/accounts_mgmt", "https://overriden.her.server.com", 608 )) 609 Expect(connection.TokenURL()).To(Equal("https://overriden.openid.server.com")) 610 user, password := connection.User() 611 Expect(user).To(Equal("overriden.myuser")) 612 Expect(password).To(Equal("overriden.mypassword")) 613 client, secret := connection.Client() 614 Expect(client).To(Equal("overriden.myclient")) 615 Expect(secret).To(Equal("overriden.mysecret")) 616 returnedAccess, returnedRefresh, err := connection.Tokens() 617 Expect(err).ToNot(HaveOccurred()) 618 Expect(returnedAccess).To(Equal(overridenAccess)) 619 Expect(returnedRefresh).To(Equal(overridenRefresh)) 620 defer func() { 621 err = connection.Close() 622 Expect(err).ToNot(HaveOccurred()) 623 }() 624 Expect(connection.Scopes()).To(ConsistOf("openid", "overriden.myscope")) 625 Expect(connection.Insecure()).To(BeFalse()) 626 Expect(connection.Agent()).To(Equal("overriden.myagent")) 627 Expect(connection.RetryLimit()).To(Equal(4)) 628 Expect(connection.MetricsSubsystem()).To(Equal("overriden_mysubsystem")) 629 }) 630 631 It("Method calls before load don't override configuration file", func() { 632 // Create temporary files for the trusted CAs: 633 tmp, err := os.MkdirTemp("", "*.test.cas") 634 Expect(err).ToNot(HaveOccurred()) 635 defer func() { 636 err = os.RemoveAll(tmp) 637 Expect(err).ToNot(HaveOccurred()) 638 }() 639 err = os.WriteFile(filepath.Join(tmp, "myca.pem"), mycaPEM, 0600) 640 Expect(err).ToNot(HaveOccurred()) 641 err = os.WriteFile(filepath.Join(tmp, "yourca.pem"), yourcaPEM, 0600) 642 Expect(err).ToNot(HaveOccurred()) 643 644 // Create a temporary YAML file containing the configuration: 645 fileAccess := MakeTokenString("Bearer", 5*time.Minute) 646 fileRefresh := MakeTokenString("Refresh", 10*time.Hour) 647 content := EvaluateTemplate( 648 ` 649 url: https://my.server.com 650 alternative_urls: 651 /api/clusters_mgmt: https://your.server.com 652 /api/accounts_mgmt: https://her.server.com 653 token_url: https://openid.server.com 654 user: myuser 655 password: mypassword 656 client_id: myclient 657 client_secret: mysecret 658 tokens: 659 - {{ .AccessToken }} 660 - {{ .RefreshToken }} 661 scopes: 662 - openid 663 - myscope 664 insecure: true 665 trusted_cas: 666 - {{ .Tmp }}/myca.pem 667 - {{ .Tmp }}/yourca.pem 668 agent: myagent 669 retry_limit: 5 670 metrics_subsystem: mysubsystem 671 `, 672 "Tmp", tmp, 673 "AccessToken", fileAccess, 674 "RefreshToken", fileRefresh, 675 ) 676 file, err := os.CreateTemp("", "*.yaml") 677 Expect(err).ToNot(HaveOccurred()) 678 path := file.Name() 679 defer func() { 680 err = os.Remove(path) 681 Expect(err).ToNot(HaveOccurred()) 682 }() 683 _, err = file.WriteString(content) 684 Expect(err).ToNot(HaveOccurred()) 685 err = file.Close() 686 Expect(err).ToNot(HaveOccurred()) 687 688 // Configure the connection with methods call and then load the configuration file: 689 overridenAccess := MakeTokenString("Bearer", 5*time.Minute) 690 overridenRefresh := MakeTokenString("Refresh", 10*time.Hour) 691 connection, err := NewConnectionBuilder(). 692 Logger(logger). 693 URL("https://overriden.my.server.com"). 694 AlternativeURL("/api/clusters_mgmt", "https://overriden.your.server.com"). 695 AlternativeURL("/api/accounts_mgmt", "https://overriden.her.server.com"). 696 TokenURL("https://overriden.openid.server.com"). 697 User("overriden.myuser", "overriden.mypassword"). 698 Client("overriden.myclient", "overriden.mysecret"). 699 Tokens(overridenAccess, overridenRefresh). 700 Scopes("openid", "overriden.myscope"). 701 Insecure(false). 702 Agent("overriden.myagent"). 703 RetryLimit(4). 704 MetricsSubsystem("overriden_mysubsystem"). 705 Load(path). 706 Build() 707 Expect(err).ToNot(HaveOccurred()) 708 709 // Check that the actual settings are the ones from the file: 710 Expect(connection.URL()).To(Equal("https://my.server.com")) 711 alternativeURLs := connection.AlternativeURLs() 712 Expect(alternativeURLs).To(HaveLen(2)) 713 Expect(alternativeURLs).To(HaveKeyWithValue( 714 "/api/clusters_mgmt", "https://your.server.com", 715 )) 716 Expect(alternativeURLs).To(HaveKeyWithValue( 717 "/api/accounts_mgmt", "https://her.server.com", 718 )) 719 Expect(connection.TokenURL()).To(Equal("https://openid.server.com")) 720 user, password := connection.User() 721 Expect(user).To(Equal("myuser")) 722 Expect(password).To(Equal("mypassword")) 723 client, secret := connection.Client() 724 Expect(client).To(Equal("myclient")) 725 Expect(secret).To(Equal("mysecret")) 726 returnedAccess, returnedRefresh, err := connection.Tokens() 727 Expect(err).ToNot(HaveOccurred()) 728 Expect(returnedAccess).To(Equal(fileAccess)) 729 Expect(returnedRefresh).To(Equal(fileRefresh)) 730 defer func() { 731 err = connection.Close() 732 Expect(err).ToNot(HaveOccurred()) 733 }() 734 Expect(connection.Scopes()).To(ConsistOf("openid", "myscope")) 735 Expect(connection.Insecure()).To(BeTrue()) 736 Expect(connection.Agent()).To(Equal("myagent")) 737 Expect(connection.RetryLimit()).To(Equal(5)) 738 Expect(connection.MetricsSubsystem()).To(Equal("mysubsystem")) 739 }) 740 741 It("Returns configured URL when there are no alternative URLs", func() { 742 token := MakeTokenString("Bearer", 5*time.Minute) 743 connection, err := NewConnectionBuilder(). 744 Logger(logger). 745 URL("https://my.server.com"). 746 Tokens(token). 747 Build() 748 Expect(err).ToNot(HaveOccurred()) 749 Expect(connection.URL()).To(Equal("https://my.server.com")) 750 }) 751 752 It("Returns configured URL when there are alternative URLs", func() { 753 token := MakeTokenString("Bearer", 5*time.Minute) 754 connection, err := NewConnectionBuilder(). 755 Logger(logger). 756 URL("https://my.server.com"). 757 AlternativeURL("/api/clusters_mgmt", "https://your.server.com"). 758 AlternativeURL("/api/accounts_mgmt", "https://her.server.com"). 759 Tokens(token). 760 Build() 761 Expect(err).ToNot(HaveOccurred()) 762 Expect(connection.URL()).To(Equal("https://my.server.com")) 763 }) 764 765 It("Can't be created with URL without scheme", func() { 766 token := MakeTokenString("Bearer", 5*time.Minute) 767 connection, err := NewConnectionBuilder(). 768 Logger(logger). 769 URL("my.server.com"). 770 Tokens(token). 771 Build() 772 Expect(err).To(HaveOccurred()) 773 Expect(connection).To(BeNil()) 774 message := err.Error() 775 Expect(message).To(ContainSubstring("my.server.com")) 776 Expect(message).To(ContainSubstring("scheme")) 777 Expect(message).To(ContainSubstring("http")) 778 Expect(message).To(ContainSubstring("https")) 779 }) 780 781 It("Can't be created with URL with wrong scheme", func() { 782 token := MakeTokenString("Bearer", 5*time.Minute) 783 connection, err := NewConnectionBuilder(). 784 Logger(logger). 785 URL("junk://my.server.com"). 786 Tokens(token). 787 Build() 788 Expect(err).To(HaveOccurred()) 789 Expect(connection).To(BeNil()) 790 message := err.Error() 791 Expect(message).To(ContainSubstring("junk://my.server.com")) 792 Expect(message).To(ContainSubstring("scheme")) 793 Expect(message).To(ContainSubstring("http")) 794 Expect(message).To(ContainSubstring("https")) 795 Expect(message).To(ContainSubstring("junk")) 796 }) 797 798 It("Can't be created with alternative URL wihout scheme", func() { 799 token := MakeTokenString("Bearer", 5*time.Minute) 800 connection, err := NewConnectionBuilder(). 801 Logger(logger). 802 AlternativeURL("/api/clusters_mtmt", "my.server.com"). 803 Tokens(token). 804 Build() 805 Expect(err).To(HaveOccurred()) 806 Expect(connection).To(BeNil()) 807 message := err.Error() 808 Expect(message).To(ContainSubstring("my.server.com")) 809 Expect(message).To(ContainSubstring("scheme")) 810 Expect(message).To(ContainSubstring("http")) 811 Expect(message).To(ContainSubstring("https")) 812 }) 813 814 It("Can't be created with alternative URL with wrong scheme", func() { 815 token := MakeTokenString("Bearer", 5*time.Minute) 816 connection, err := NewConnectionBuilder(). 817 Logger(logger). 818 AlternativeURL("/api/clusters_mtmt", "junk://my.server.com"). 819 Tokens(token). 820 Build() 821 Expect(err).To(HaveOccurred()) 822 Expect(connection).To(BeNil()) 823 message := err.Error() 824 Expect(message).To(ContainSubstring("junk://my.server.com")) 825 Expect(message).To(ContainSubstring("scheme")) 826 Expect(message).To(ContainSubstring("http")) 827 Expect(message).To(ContainSubstring("https")) 828 Expect(message).To(ContainSubstring("junk")) 829 }) 830 831 It("Can't be created with URL without host name", func() { 832 token := MakeTokenString("Bearer", 5*time.Minute) 833 _, err := NewConnectionBuilder(). 834 Logger(logger). 835 URL("http:///mypath"). 836 Tokens(token). 837 Build() 838 Expect(err).To(HaveOccurred()) 839 message := err.Error() 840 Expect(message).To(ContainSubstring("http:///mypath")) 841 Expect(message).To(ContainSubstring("host name")) 842 }) 843 844 It("Can't be created with alternative URL without host name", func() { 845 token := MakeTokenString("Bearer", 5*time.Minute) 846 connection, err := NewConnectionBuilder(). 847 Logger(logger). 848 AlternativeURL("/api/clusters_mgmt", "http:///mypath"). 849 Tokens(token). 850 Build() 851 Expect(err).To(HaveOccurred()) 852 Expect(connection).To(BeNil()) 853 message := err.Error() 854 Expect(message).To(ContainSubstring("http:///mypath")) 855 Expect(message).To(ContainSubstring("host name")) 856 }) 857 858 It("Can't be created with token URL without scheme", func() { 859 token := MakeTokenString("Bearer", 5*time.Minute) 860 connection, err := NewConnectionBuilder(). 861 Logger(logger). 862 URL("https://my.server.com"). 863 TokenURL("your.server.com"). 864 Tokens(token). 865 Build() 866 Expect(err).To(HaveOccurred()) 867 Expect(connection).To(BeNil()) 868 message := err.Error() 869 Expect(message).To(ContainSubstring("your.server.com")) 870 Expect(message).To(ContainSubstring("scheme")) 871 Expect(message).To(ContainSubstring("http")) 872 Expect(message).To(ContainSubstring("https")) 873 }) 874 875 It("Can't be created with token URL with wrong scheme", func() { 876 token := MakeTokenString("Bearer", 5*time.Minute) 877 connection, err := NewConnectionBuilder(). 878 Logger(logger). 879 URL("https://my.server.com"). 880 TokenURL("junk://your.server.com"). 881 Tokens(token). 882 Build() 883 Expect(err).To(HaveOccurred()) 884 Expect(connection).To(BeNil()) 885 message := err.Error() 886 Expect(message).To(ContainSubstring("junk://your.server.com")) 887 Expect(message).To(ContainSubstring("scheme")) 888 Expect(message).To(ContainSubstring("http")) 889 Expect(message).To(ContainSubstring("https")) 890 Expect(message).To(ContainSubstring("junk")) 891 }) 892 893 It("Can't be created with token URL without host name", func() { 894 token := MakeTokenString("Bearer", 5*time.Minute) 895 connection, err := NewConnectionBuilder(). 896 Logger(logger). 897 URL("http://my.server.com"). 898 TokenURL("http:///yourpath"). 899 Tokens(token). 900 Build() 901 Expect(err).To(HaveOccurred()) 902 Expect(connection).To(BeNil()) 903 message := err.Error() 904 Expect(message).To(ContainSubstring("http:///yourpath")) 905 Expect(message).To(ContainSubstring("host name")) 906 }) 907 908 It("Can be created with Unix network and host", func() { 909 token := MakeTokenString("Bearer", 5*time.Minute) 910 connection, err := NewConnectionBuilder(). 911 Logger(logger). 912 URL("unix://my.server.com/tmp/api.socket"). 913 Tokens(token). 914 Build() 915 Expect(err).ToNot(HaveOccurred()) 916 Expect(connection).ToNot(BeNil()) 917 }) 918 919 It("Can be created with Unix network and HTTPS", func() { 920 token := MakeTokenString("Bearer", 5*time.Minute) 921 connection, err := NewConnectionBuilder(). 922 Logger(logger). 923 URL("unix+https://my.server.com/tmp/api.socket"). 924 Tokens(token). 925 Build() 926 Expect(err).ToNot(HaveOccurred()) 927 Expect(connection).ToNot(BeNil()) 928 }) 929 930 It("Can't be created with Unix network and no host", func() { 931 token := MakeTokenString("Bearer", 5*time.Minute) 932 connection, err := NewConnectionBuilder(). 933 Logger(logger). 934 URL("unix:/tmp/api.socket"). 935 Tokens(token). 936 Build() 937 Expect(err).To(HaveOccurred()) 938 Expect(connection).To(BeNil()) 939 message := err.Error() 940 Expect(message).To(ContainSubstring("unix:/tmp/api.socket")) 941 Expect(message).To(ContainSubstring("host")) 942 Expect(message).To(ContainSubstring("mandatory")) 943 }) 944 945 It("Can't be created with incorrect network", func() { 946 token := MakeTokenString("Bearer", 5*time.Minute) 947 connection, err := NewConnectionBuilder(). 948 Logger(logger). 949 URL("junk+https://my.server.com"). 950 Tokens(token). 951 Build() 952 Expect(err).To(HaveOccurred()) 953 Expect(connection).To(BeNil()) 954 message := err.Error() 955 Expect(message).To(ContainSubstring("junk")) 956 Expect(message).To(ContainSubstring("network")) 957 Expect(message).To(ContainSubstring("tcp")) 958 Expect(message).To(ContainSubstring("unix")) 959 }) 960 961 It("Can't be created with Unix network and no socket", func() { 962 token := MakeTokenString("Bearer", 5*time.Minute) 963 connection, err := NewConnectionBuilder(). 964 Logger(logger). 965 URL("unix://my.server.com"). 966 Tokens(token). 967 Build() 968 Expect(err).To(HaveOccurred()) 969 Expect(connection).To(BeNil()) 970 message := err.Error() 971 Expect(message).To(ContainSubstring("unix")) 972 Expect(message).To(ContainSubstring("socket")) 973 Expect(message).To(ContainSubstring("path")) 974 }) 975 976 It("Function Close returns nil when trying to close a closed connection", func() { 977 offlineToken := MakeTokenString("Offline", 0) 978 connection, err := NewConnectionBuilder(). 979 Logger(logger). 980 Tokens(offlineToken). 981 Build() 982 Expect(err).ToNot(HaveOccurred()) 983 err = connection.Close() 984 Expect(err).ToNot(HaveOccurred()) 985 // Try to close the connection again 986 err = connection.Close() 987 Expect(err).To(BeNil()) 988 }) 989 990 It("Can be created with no authentication", func() { 991 connection, err := NewUnauthenticatedConnectionBuilder(). 992 Logger(logger). 993 Build() 994 Expect(err).ToNot(HaveOccurred()) 995 996 Expect(connection).ToNot(BeNil()) 997 Expect(connection.Scopes()).To(BeEmpty()) 998 Expect(connection.TokenURL()).To(BeEmpty()) 999 clientId, clientSecret := connection.Client() 1000 Expect(clientId).To(BeEmpty()) 1001 Expect(clientSecret).To(BeEmpty()) 1002 user, password := connection.User() 1003 Expect(user).To(BeEmpty()) 1004 Expect(password).To(BeEmpty()) 1005 access, refresh, err := connection.Tokens() 1006 Expect(access).To(BeEmpty()) 1007 Expect(refresh).To(BeEmpty()) 1008 Expect(err).ToNot(HaveOccurred()) 1009 access, refresh, err = connection.TokensContext(context.Background()) 1010 Expect(access).To(BeEmpty()) 1011 Expect(refresh).To(BeEmpty()) 1012 Expect(err).ToNot(HaveOccurred()) 1013 1014 err = connection.Close() 1015 Expect(err).ToNot(HaveOccurred()) 1016 }) 1017 }) 1018 1019 type TestTransport struct { 1020 called int 1021 } 1022 1023 func (t *TestTransport) RoundTrip(request *http.Request) (response *http.Response, err error) { 1024 t.called++ 1025 header := http.Header{} 1026 header.Add("Content-type", "application/json") 1027 response = &http.Response{ 1028 StatusCode: http.StatusInternalServerError, 1029 Header: header, 1030 Body: BufferWithBytes([]byte("{}")), 1031 } 1032 return response, nil 1033 } 1034 1035 func NewTestTransport() *TestTransport { 1036 return &TestTransport{called: 0} 1037 } 1038 1039 // This certificate is used only to verify that the connection can load a certificate from 1040 // a file. It isn't valid for any other thing. It has been generated with the following 1041 // commmad: 1042 // 1043 // openssl req \ 1044 // -x509 \ 1045 // -newkey rsa:4096 \ 1046 // -keyout myca.key \ 1047 // -nodes \ 1048 // -out myca.crt \ 1049 // -subj '/CN=myca.com' \ 1050 // -days 3650 1051 var mycaPEM = []byte(` 1052 -----BEGIN CERTIFICATE----- 1053 MIIFCzCCAvOgAwIBAgIUEy+dp9sWPdu59sahER7AxvapYOgwDQYJKoZIhvcNAQEL 1054 BQAwFTETMBEGA1UEAwwKeW91cmNhLmNvbTAeFw0yMDExMTgxNjUwMTNaFw0zMDEx 1055 MTYxNjUwMTNaMBUxEzARBgNVBAMMCnlvdXJjYS5jb20wggIiMA0GCSqGSIb3DQEB 1056 AQUAA4ICDwAwggIKAoICAQC7IXXeem/arTEAvujthKpzxMOaimpIq276rIaaehSf 1057 PKfwwFScz6KzkcRcCjzGlmQIUfe0VunL9xMfWcOBQ8u0LofJpcRE+AuYXdgAuuyH 1058 ijWukGZ4o1QGoSmS90TVOOLGA38gnPbQTAgJN8DzxccoOTVtdqsAZMK5zKGJ0IUa 1059 0ZkPJvs0QYfVYMgYCjiRCeTNze4Cwb/ecj9CZump2IUNm3oE28dUkzRCBysNAvKu 1060 Ms9HktnQft7BxoefdCZK04o8a1BLzZVSPe7qV+bk0+hD1xygoEPATzhuGl7Al9EI 1061 lkOJ8fv3uompnz5bHOeP2dNuwn9efeINtJcLlx8wySkU0oNTqQq9MVI7gRwUUAT0 1062 dLzETULngRhvjGSYEyST3vT27V444fVYkSVIjmji+SmzSejfZq/A1NTQ8M9TUWIs 1063 7dL562GJnsalPnI+m9XR5m3oajY+CYtcd5q1iIus+WrMXups8fQnpssJHioFs86s 1064 NEQ0Evbl0OGxxYivJwKbT6Oo86Uh3nUXXx/xxBI5HRmQap38EK6K0WZR3V7MBnpF 1065 xVv5vUO/zXc7DxAghcXb41XSTWGOM+AqaCIv+zys87/F6x1dmCkA8DCNxYlEeK88 1066 6xHuK5265iu/to9NSvzCAxmrz4fDea3eAxpZus39yN3N2ud2IAlMguicjZgLF3mg 1067 uQIDAQABo1MwUTAdBgNVHQ4EFgQUf37ek+qeiMRhZ0q6whksuPY4ezQwHwYDVR0j 1068 BBgwFoAUf37ek+qeiMRhZ0q6whksuPY4ezQwDwYDVR0TAQH/BAUwAwEB/zANBgkq 1069 hkiG9w0BAQsFAAOCAgEArP/sjXMURWamJFckKwpam+w8WPW3b0wq6GkQky6XDcXK 1070 sym5vJfQtQgzZV/rxb0RcO4ywPKYJK2ViREqksmlD5XLL/6grbe+rcIY55IVcFKe 1071 3ZfE7toa08gWV8kb9VP2KVNt5505jJUVtF8FxRxsu5W0x6b6Kegyotsd5/7tads9 1072 9qMOoiWOyWxdP4FZalNM8PXaF8pspNqeo/cvgFWvDTqtFvgH4vkLehMueAWmDML2 1073 lqYHCaMworpY3e8vfk6jK8b9fRmuXaGMlOTpY7XoF8OOSMI1LdPVSO/lo8DCJbge 1074 RoBHZ97fA8ShB+WRjBuAuh5ST/TEqTha+razhmauVT6CYtw9SSC0SK0ZbNg2oZoG 1075 CzrQhYf9gHXXPnp0qsuPbtHrMm3DPBHBUrfrlvVVmWjKCVipFdPyRXth9FtNguZn 1076 d5NUX3JwGRoez/xHv8nHyjdKmTCu8pmP/9SAoV/HUgpcSaEtXyZdlNd+SgIcgV/A 1077 00eSLesNr9/auzWklxh92oqDnd96IRueamFm6W0BCh64if//BCmAFelPHlWSP1ws 1078 nzNA++GMw2OXJ1cE/9GTU3or0eDGRgB9XIu+T/SLPXW9xBm0hZdt5gyYfEDmppHa 1079 nctMPznTWc+iYCMAwroHzJV40ZrVhllhNYrrOLigA7NfAiXelLmSbLx316TnoZM= 1080 -----END CERTIFICATE----- 1081 `) 1082 1083 // This certificate is used only to verify that the connection can load a certificate from 1084 // a file. It isn't valid for any other thing. It has been generated with the following 1085 // commmad: 1086 // 1087 // openssl req \ 1088 // -x509 \ 1089 // -newkey rsa:4096 \ 1090 // -keyout yourca.key \ 1091 // -nodes \ 1092 // -out yourca.crt \ 1093 // -subj '/CN=yourca.com' \ 1094 // -days 3650 1095 var yourcaPEM = []byte(` 1096 -----BEGIN CERTIFICATE----- 1097 MIIFCzCCAvOgAwIBAgIUOBKDkme46UAOif9G7fIfN0FTmAswDQYJKoZIhvcNAQEL 1098 BQAwFTETMBEGA1UEAwwKeW91cmNhLmNvbTAeFw0yMDExMTgxNjU0MTBaFw0zMDEx 1099 MTYxNjU0MTBaMBUxEzARBgNVBAMMCnlvdXJjYS5jb20wggIiMA0GCSqGSIb3DQEB 1100 AQUAA4ICDwAwggIKAoICAQCjCUIsLIE0gp7JQZHvwrl12IjYEQjJWEpsEbp/jpux 1101 ztUAVju5Cq8+V5DYIzHi6WHlottpp6obh4TaCHZroZxwXKCoUARJwtPqADhDX+tr 1102 Jy8gA2y5ixGxryyXVsAT3YkEBW05i82aKa+FB05T0eUS52SBS2V6Fd4XU19denx5 1103 rb1eFNor/rmG0gMCAsh/4oWw8DdBEU0qc/9vQ3lsWvGU1noYt/kwfAcaSydrqaIA 1104 EvK/sG1/hxWD+JBOUwrah0zxT+7x6FbzqX83m03HM7ZHJR+qNjtg31loQwocsn20 1105 qOM3vMQkqyqjnXMHtIleyhw7fWNqQcvS/f+A3QUosf3h90c/BVmoJ+QUsm9gOUFT 1106 jGWybFWRnukSY/CMazVQvSF3N6GDz0iQQFQEwtpLhe0UhpFs/UUOXUi2+JjLRkLf 1107 fIeyk+K9EAz8Nd+vOMgr7ud70MykF7X5FzGLwJfz5bj62XVVifA8yMNoxIdlRFZp 1108 H8OXzMwUO0+ktCf1StXCEV8/HoBP8BeKRl/PPqyHlY2rqXrNYq+ZXR+I25HWJcyJ 1109 UUWQBq66yfxEIR2L5tJoVf0P7Z+WcplX7bo8T06n92zV5pU6WPi2+xAob2cmR9M4 1110 thlHl5uUDIYRgXZ2RQnMhea2GcDtFi8zfeWIJTZ54CmUi326sToqvCbOrHMkxus9 1111 xwIDAQABo1MwUTAdBgNVHQ4EFgQU5anvmjhvY6yPqy9qKU6cZAjX88MwHwYDVR0j 1112 BBgwFoAU5anvmjhvY6yPqy9qKU6cZAjX88MwDwYDVR0TAQH/BAUwAwEB/zANBgkq 1113 hkiG9w0BAQsFAAOCAgEAH2x/DTPnfxw06RZmjGCWOJJOUiO9uW4dVtLUzdCmOkuX 1114 zngsqAB6Mqy+GXb4jsNdKdVR74Lb/9gv9WkXTxLPnW8sBmxC7NxjJZbQVlHuQK3U 1115 s8GGo1wwuR/kCcnckdRAuDf8BbllqpPq+zUm1ZzM2NnMtiptkojxpleJugttXGiw 1116 SHRh6hY3RZAKD6s6eyg67O6Dx3bFgyzYt91cG+YJPbSQh9hBhhlmp5GwOMg27u6N 1117 skSNZIK2SNs5Bael+WfiUiEB1cFwUc0TYPUSJEkLXvLcqqVzuj53IEU7UCifYqHQ 1118 xlhdROagJc8fSOQ0yEEwBPqDVRT3fJipAGRB7h8a0pEtfbD8M0df6DGkcRvOf/My 1119 B2Ss8ZrL+tLDKEJji6aZXlkFbs6aKko0cKbZQvquISgEdcZp3hQ4oc+eUmkOfkk6 1120 0D+7ZY3m4JDAr38tVDw3lG2I3THvmT8zdPZzujkZjHUvVqWaoEX1k6IUSZ5DnQ3H 1121 NIf/SfR7aXBeCsoJnCE+nsN/ba2twS8Wx1evuWDdlVGYrE4ujXpCAKspwi0mPirx 1122 bkAQVDU7e/Zr6P8ZI9P4w1MYZvKagPo1+hCCiEAaeNdgMoOwQbLpw6py0x7B+mxI 1123 ppX5DVNV8wJAb9KqeSXwd89Z5unpeS6KZsMcb5qiK60Lj12aLmZ8ip6s7xjAS8Q= 1124 -----END CERTIFICATE----- 1125 `)