github.com/wanddynosios/cli@v7.1.0+incompatible/integration/v6/isolated/login_command_test.go (about) 1 package isolated 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 "os/exec" 10 "path/filepath" 11 "regexp" 12 "time" 13 14 "code.cloudfoundry.org/cli/integration/helpers" 15 "code.cloudfoundry.org/cli/util/configv3" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 . "github.com/onsi/gomega/gbytes" 19 . "github.com/onsi/gomega/gexec" 20 "github.com/onsi/gomega/ghttp" 21 ) 22 23 var _ = Describe("login command", func() { 24 BeforeEach(func() { 25 helpers.SkipIfClientCredentialsTestMode() 26 }) 27 28 Describe("Help Text", func() { 29 When("--help flag is set", func() { 30 It("displays the command usage", func() { 31 session := helpers.CF("login", "--help") 32 Eventually(session).Should(Exit(0)) 33 34 Expect(session).Should(Say("NAME:\n")) 35 Expect(session).Should(Say("login - Log user in")) 36 37 Expect(session).Should(Say("USAGE:\n")) 38 Expect(session).Should(Say(`cf login \[-a API_URL\] \[-u USERNAME\] \[-p PASSWORD\] \[-o ORG\] \[-s SPACE\] \[--sso | --sso-passcode PASSCODE\] \[--origin ORIGIN\]`)) 39 40 Expect(session).Should(Say("WARNING:\n")) 41 Expect(session).Should(Say("Providing your password as a command line option is highly discouraged\n")) 42 Expect(session).Should(Say("Your password may be visible to others and may be recorded in your shell history\n")) 43 44 Expect(session).Should(Say("EXAMPLES:\n")) 45 Expect(session).Should(Say(regexp.QuoteMeta("cf login (omit username and password to login interactively -- cf will prompt for both)"))) 46 Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p pa55woRD (specify username and password as arguments)"))) 47 Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p \"my password\" (use quotes for passwords with a space)"))) 48 Expect(session).Should(Say(regexp.QuoteMeta("cf login -u name@example.com -p \"\\\"password\\\"\" (escape quotes if used in password)"))) 49 Expect(session).Should(Say(regexp.QuoteMeta("cf login --sso (cf will provide a url to obtain a one-time passcode to login)"))) 50 Expect(session).Should(Say(regexp.QuoteMeta("cf login --origin ldap"))) 51 52 Expect(session).Should(Say("ALIAS:\n")) 53 Expect(session).Should(Say("l")) 54 55 Expect(session).Should(Say("OPTIONS:\n")) 56 Expect(session).Should(Say(`-a\s+API endpoint \(e.g. https://api\.example\.com\)`)) 57 Expect(session).Should(Say(`-o\s+Org`)) 58 Expect(session).Should(Say(`-p\s+Password`)) 59 Expect(session).Should(Say(`-s\s+Space`)) 60 Expect(session).Should(Say(`--skip-ssl-validation\s+Skip verification of the API endpoint\. Not recommended\!`)) 61 Expect(session).Should(Say(`--sso\s+Prompt for a one-time passcode to login`)) 62 Expect(session).Should(Say(`--sso-passcode\s+One-time passcode`)) 63 Expect(session).Should(Say(`-u\s+Username`)) 64 65 Expect(session).Should(Say("SEE ALSO:\n")) 66 Expect(session).Should(Say("api, auth, target")) 67 }) 68 }) 69 }) 70 71 Describe("Invalid Command Usage", func() { 72 When("a random flag is passed in", func() { 73 It("exits 1 and displays an unknown flag error message", func() { 74 session := helpers.CF("login", "--test") 75 Eventually(session).Should(Exit(1)) 76 77 Expect(session.Err).Should(Say("Incorrect Usage: unknown flag `test'")) 78 }) 79 }) 80 }) 81 82 Describe("authorization endpoint", func() { 83 When("an authorization endpoint is advertised by the server", func() { 84 var server *ghttp.Server 85 86 BeforeEach(func() { 87 server = helpers.StartMockServerWithCustomAuthorizationEndpoint("/custom/authorization/endpoint") 88 89 fakeTokenResponse := map[string]string{ 90 "access_token": helpers.BuildTokenString(time.Now().Add(time.Hour)), 91 "token_type": "bearer", 92 "refresh_token": "refresh-token", 93 } 94 server.RouteToHandler(http.MethodGet, "/login", 95 func(http.ResponseWriter, *http.Request) { 96 Fail("The advertized authorization_endpoint should be used in preference to the UAA endpoint") 97 }, 98 ) 99 server.RouteToHandler(http.MethodGet, "/v3/organizations", 100 ghttp.RespondWith(http.StatusOK, `{"total_results": 0, "total_pages": 1, "resources": []}`)) 101 102 server.AppendHandlers( 103 ghttp.CombineHandlers( 104 ghttp.VerifyRequest("POST", "/custom/authorization/endpoint/oauth/token"), 105 ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse), 106 ), 107 ) 108 }) 109 110 AfterEach(func() { 111 server.Close() 112 }) 113 114 It("requests a token from the authorization endpoint and exits successfully", func() { 115 username, password := helpers.GetCredentials() 116 session := helpers.CF("-v", "login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 117 Eventually(session).Should(Exit(0)) 118 }) 119 }) 120 }) 121 122 Describe("Minimum Version Check", func() { 123 When("the v2 API version is less than the minimum supported version", func() { 124 var server *ghttp.Server 125 126 BeforeEach(func() { 127 server = helpers.StartMockServerWithAPIVersions("2.99.9", "3.36.0") 128 129 fakeTokenResponse := map[string]string{ 130 "access_token": helpers.BuildTokenString(time.Now()), 131 "token_type": "bearer", 132 "refresh_token": "refresh-token", 133 } 134 server.RouteToHandler(http.MethodPost, "/oauth/token", 135 ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse)) 136 server.RouteToHandler(http.MethodGet, "/v3/organizations", 137 ghttp.RespondWith(http.StatusOK, `{ 138 "total_results": 0, 139 "total_pages": 1, 140 "resources": []}`)) 141 }) 142 143 AfterEach(func() { 144 server.Close() 145 }) 146 147 It("displays the warning to stderr and exits successfully", func() { 148 username, password := helpers.GetCredentials() 149 session := helpers.CF("login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 150 151 Eventually(session.Err).Should(Say("Your CF API version .+ is no longer supported. Upgrade to a newer version of the API .+")) 152 Eventually(session).Should(Exit(0)) 153 }) 154 155 It("displays the warning before authenticating", func() { 156 username, password := helpers.GetCredentials() 157 cmd := exec.Command("cf", "login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 158 stdErrAndStdOut, err := cmd.CombinedOutput() 159 Expect(err).ToNot(HaveOccurred()) 160 161 output := BufferWithBytes(stdErrAndStdOut) 162 Expect(output).To(Say("Your CF API version .+ is no longer supported. Upgrade to a newer version of the API .+")) 163 Expect(output).To(Say("Authenticating..."), "Expected Min API version warning before 'Authenticating...' message.") 164 }) 165 }) 166 167 When("the CLI version is lower than the minimum supported version by the CC", func() { 168 var server *ghttp.Server 169 170 BeforeEach(func() { 171 server = helpers.StartMockServerWithMinimumCLIVersion("9000.0.0") 172 173 fakeTokenResponse := map[string]string{ 174 "access_token": "", 175 "token_type": "bearer", 176 } 177 server.RouteToHandler(http.MethodPost, "/oauth/token", 178 ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse)) 179 server.RouteToHandler(http.MethodGet, "/v3/organizations", 180 ghttp.RespondWith(http.StatusOK, `{ 181 "total_results": 0, 182 "total_pages": 1, 183 "resources": []}`)) 184 }) 185 186 AfterEach(func() { 187 server.Close() 188 }) 189 190 It("displays the warning to stderr and exits successfully", func() { 191 username, password := helpers.GetCredentials() 192 session := helpers.CF("login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 193 194 Eventually(session.Err).Should(Say(`Cloud Foundry API version .+ requires CLI version .+\. You are currently on version .+\. To upgrade your CLI, please visit: https://github.com/cloudfoundry/cli#downloads`)) 195 Eventually(session).Should(Exit(0)) 196 }) 197 198 It("displays the warning before authenticating", func() { 199 username, password := helpers.GetCredentials() 200 cmd := exec.Command("cf", "login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 201 stdErrAndStdOut, err := cmd.CombinedOutput() 202 Expect(err).ToNot(HaveOccurred()) 203 204 output := BufferWithBytes(stdErrAndStdOut) 205 Expect(output).To(Say(`Cloud Foundry API version .+ requires CLI version .+\. You are currently on version .+\. To upgrade your CLI, please visit: https://github.com/cloudfoundry/cli#downloads`)) 206 Expect(output).To(Say("Authenticating..."), "Expected Min CLI version warning before 'Authenticating...' message.") 207 }) 208 }) 209 }) 210 211 Describe("API Endpoint", func() { 212 When("the API endpoint is not set", func() { 213 BeforeEach(func() { 214 helpers.UnsetAPI() 215 }) 216 217 When("the user does not provide the -a flag", func() { 218 It("prompts the user for an endpoint", func() { 219 input := NewBuffer() 220 _, err := input.Write([]byte("\n")) 221 Expect(err).ToNot(HaveOccurred()) 222 session := helpers.CFWithStdin(input, "login") 223 Eventually(session).Should(Say("API endpoint:")) 224 session.Interrupt() 225 Eventually(session).Should(Exit()) 226 }) 227 228 When("the API endpoint provided at the prompt is unreachable", func() { 229 It("returns an error", func() { 230 input := NewBuffer() 231 _, err := input.Write([]byte("does.not.exist\n")) 232 Expect(err).ToNot(HaveOccurred()) 233 session := helpers.CFWithStdin(input, "login") 234 Eventually(session).Should(Say("API endpoint:")) 235 Eventually(session).Should(Say("FAILED")) 236 Eventually(session.Err).Should(Say("Request error: ")) 237 Eventually(session.Err).Should(Say("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection.")) 238 Eventually(session).Should(Exit(1)) 239 }) 240 }) 241 }) 242 243 When("the user provides the -a flag", func() { 244 245 var session *Session 246 BeforeEach(func() { 247 if skipSSLValidation { 248 session = helpers.CF("login", "-a", apiURL, "--skip-ssl-validation") 249 } else { 250 session = helpers.CF("login", "-a", apiURL) 251 } 252 Eventually(session).Should(Say("API endpoint: %s", apiURL)) 253 // TODO https://www.pivotaltracker.com/story/show/166938709/comments/204492216 254 //Consistently(session).ShouldNot(Say("API endpoint:")) 255 //session.Interrupt() 256 Eventually(session).Should(Exit()) 257 258 }) 259 It("sets the API endpoint and does not prompt the user for the API endpoint", func() { 260 session = helpers.CF("api") 261 Eventually(session).Should(Exit(0)) 262 Expect(session).Should(Say("api endpoint: %s", apiURL)) 263 264 }) 265 It("writes fields to the config file when targeting an API", func() { 266 rawConfig, err := ioutil.ReadFile(filepath.Join(homeDir, ".cf", "config.json")) 267 Expect(err).NotTo(HaveOccurred()) 268 269 var configFile configv3.JSONConfig 270 err = json.Unmarshal(rawConfig, &configFile) 271 Expect(err).NotTo(HaveOccurred()) 272 273 Expect(configFile.ConfigVersion).To(Equal(configv3.CurrentConfigVersion)) 274 Expect(configFile.Target).To(Equal(apiURL)) 275 Expect(configFile.APIVersion).To(MatchRegexp(`\d+\.\d+\.\d+`)) 276 Expect(configFile.AuthorizationEndpoint).ToNot(BeEmpty()) 277 Expect(configFile.DopplerEndpoint).To(MatchRegexp("^wss://")) 278 Expect(configFile.LogCacheEndpoint).To(MatchRegexp(".*log-cache.*")) 279 280 }) 281 282 When("the provided API endpoint is unreachable", func() { 283 It("displays an error and fails", func() { 284 var session *Session 285 if skipSSLValidation { 286 session = helpers.CF("login", "-a", "does.not.exist", "--skip-ssl-validation") 287 } else { 288 session = helpers.CF("login", "-a", "does.not.exist") 289 } 290 291 Eventually(session).Should(Say("API endpoint: does.not.exist")) 292 Eventually(session).Should(Say("FAILED")) 293 Eventually(session.Err).Should(Say("Request error: ")) 294 Eventually(session.Err).Should(Say("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection.")) 295 Eventually(session).Should(Exit(1)) 296 }) 297 }) 298 299 When("the provided API endpoint has trailing slashes", func() { 300 It("removes the extra slashes", func() { 301 username, password := helpers.GetCredentials() 302 apiURLWithSlash := apiURL + "////" 303 session := helpers.CF("login", "-a", apiURLWithSlash, "-u", username, "-p", password, "--skip-ssl-validation") 304 Eventually(session).Should(Exit(0)) 305 306 session = helpers.CF("api") 307 Eventually(session).Should(Say("api endpoint:\\s+%s\n", apiURL)) 308 Eventually(session).Should(Exit(0)) 309 }) 310 }) 311 }) 312 }) 313 314 When("the API endpoint is already set", func() { 315 It("does not prompt the user for API endpoint", func() { 316 session := helpers.CF("login") 317 Consistently(session).ShouldNot(Say("API endpoint>")) 318 session.Interrupt() 319 Eventually(session).Should(Exit()) 320 }) 321 322 When("the user provides a new API endpoint with the -a flag", func() { 323 When("the provided API endpoint is unreachable", func() { 324 It("displays an error and does not change the API endpoint", func() { 325 var session *Session 326 if skipSSLValidation { 327 session = helpers.CF("login", "-a", "does.not.exist", "--skip-ssl-validation") 328 } else { 329 session = helpers.CF("login", "-a", "does.not.exist") 330 } 331 Eventually(session).Should(Say("API endpoint: does.not.exist")) 332 Eventually(session).Should(Say("FAILED")) 333 Eventually(session.Err).Should(Say("Request error: ")) 334 Eventually(session.Err).Should(Say("TIP: If you are behind a firewall and require an HTTP proxy, verify the https_proxy environment variable is correctly set. Else, check your network connection.")) 335 Eventually(session).Should(Exit(1)) 336 337 apiSession := helpers.CF("api") 338 Eventually(apiSession).Should(Exit(0)) 339 Eventually(apiSession).Should(Say("api endpoint: %s", apiURL)) 340 }) 341 }) 342 }) 343 }) 344 }) 345 346 Describe("https", func() { 347 348 When("no scheme is included in the API endpoint", func() { 349 var ( 350 hostname string 351 serverURL *url.URL 352 err error 353 session *Session 354 ) 355 356 BeforeEach(func() { 357 serverURL, err = url.Parse(helpers.GetAPI()) 358 Expect(err).NotTo(HaveOccurred()) 359 360 hostname = serverURL.Hostname() 361 362 username, password := helpers.GetCredentials() 363 if skipSSLValidation { 364 session = helpers.CF("login", "-u", username, "-p", password, "-a", hostname, "--skip-ssl-validation") 365 } else { 366 session = helpers.CF("login", "-u", username, "-p", password, "-a", hostname) 367 } 368 }) 369 370 It("displays the API endpoint you are about to target as https", func() { 371 Eventually(session).Should(Say("API endpoint: %s", hostname)) 372 Eventually(session).Should(Exit(0)) 373 }) 374 375 It("displays the API endpoint you have targeted as https", func() { 376 Eventually(session).Should(Say(`API endpoint:\s+https://%s\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`, hostname)) 377 Eventually(session).Should(Exit(0)) 378 }) 379 380 }) 381 382 When("the API endpoint's scheme is http", func() { 383 var httpURL string 384 385 BeforeEach(func() { 386 apiURL, err := url.Parse(helpers.GetAPI()) 387 Expect(err).NotTo(HaveOccurred()) 388 apiURL.Scheme = "http" 389 390 httpURL = apiURL.String() 391 }) 392 393 It("shows a warning to the user", func() { 394 username, password := helpers.GetCredentials() 395 session := helpers.CF("login", "-u", username, "-p", password, "-a", httpURL, "--skip-ssl-validation") 396 397 Eventually(session).Should(Say("API endpoint: %s", httpURL)) 398 Eventually(session.Err).Should(Say("Warning: Insecure http API endpoint detected: secure https API endpoints are recommended")) 399 Eventually(session).Should(Exit(0)) 400 }) 401 }) 402 403 // This test is redundant: 404 // If SKIP_SSL_VALIDATION is disabled 405 // then the test suite would have already logged in 406 // with a valid cert by the time this test gets run 407 When("the OS provides a valid SSL Certificate (Unix: SSL_CERT_FILE or SSL_CERT_DIR Environment variables) (Windows: Import-Certificate call)", func() { 408 BeforeEach(func() { 409 if skipSSLValidation { 410 Skip("SKIP_SSL_VALIDATION is enabled") 411 } 412 }) 413 414 It("trusts the cert and allows the users to log in", func() { 415 username, password := helpers.GetCredentials() 416 session := helpers.CF("login", "-u", username, "-p", password, "-a", helpers.GetAPI()) 417 Eventually(session).Should(Say("API endpoint: %s", apiURL)) 418 Eventually(session).Should(Exit()) 419 420 session = helpers.CF("api") 421 Eventually(session).Should(Exit(0)) 422 Expect(session).Should(Say("api endpoint: %s", apiURL)) 423 }) 424 }) 425 426 When("the SSL Certificate is invalid", func() { 427 var server *ghttp.Server 428 429 BeforeEach(func() { 430 cliVersion := "1.0.0" 431 server = helpers.StartMockServerWithMinimumCLIVersion(cliVersion) 432 }) 433 434 AfterEach(func() { 435 server.Close() 436 }) 437 438 It("displays a helpful error message and exits 1", func() { 439 session := helpers.CF("login", "-a", server.URL()) 440 Eventually(session).Should(Say("API endpoint: %s", server.URL())) 441 Eventually(session).Should(Say("FAILED")) 442 Eventually(session.Err).Should(Say("Invalid SSL Cert for %s", server.URL())) 443 Eventually(session.Err).Should(Say("TIP: Use 'cf login --skip-ssl-validation' to continue with an insecure API endpoint")) 444 Eventually(session).Should(Exit(1)) 445 }) 446 447 When("targeted with --skip-ssl-validation", func() { 448 BeforeEach(func() { 449 Eventually(helpers.CF("api", server.URL(), "--skip-ssl-validation")).Should(Exit(0)) 450 }) 451 452 When("logging in without --skip-ssl-validation", func() { 453 It("displays a helpful error message and exits 1", func() { 454 session := helpers.CF("login", "-a", server.URL()) 455 Eventually(session).Should(Say("API endpoint: %s", server.URL())) 456 Eventually(session).Should(Say("FAILED")) 457 Eventually(session.Err).Should(Say("Invalid SSL Cert for %s", server.URL())) 458 Eventually(session.Err).Should(Say("TIP: Use 'cf login --skip-ssl-validation' to continue with an insecure API endpoint")) 459 Eventually(session).Should(Exit(1)) 460 }) 461 }) 462 }) 463 464 When("the server accepts logins", func() { 465 BeforeEach(func() { 466 fakeTokenResponse := map[string]string{ 467 "access_token": "", 468 "token_type": "bearer", 469 } 470 server.RouteToHandler(http.MethodPost, "/oauth/token", 471 ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse)) 472 server.RouteToHandler(http.MethodGet, "/v3/organizations", 473 ghttp.RespondWith(http.StatusOK, `{ 474 "total_results": 0, 475 "total_pages": 1, 476 "resources": []}`)) 477 }) 478 479 It("doesn't complain about an invalid cert when we specify --skip-ssl-validation", func() { 480 session := helpers.CF("login", "-a", server.URL(), "--skip-ssl-validation") 481 //session := helpers.CF("api", server.URL(), "--skip-ssl-validation") 482 Eventually(session).Should(Exit(0)) 483 Expect(session).Should(Say("API endpoint:\\s+" + server.URL())) 484 Expect(session).Should(Say(`Authenticating\.\.\.`)) 485 Expect(session).Should(Say(`OK`)) 486 Expect(session).Should(Say(`API endpoint:\s+` + server.URL() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 487 488 Expect(string(session.Err.Contents())).Should(Not(ContainSubstring("Invalid SSL Cert for %s", server.URL()))) 489 }) 490 491 }) 492 }) 493 }) 494 495 Describe("SSO", func() { 496 When("--sso-passcode is provided", func() { 497 Context("and --sso is also passed", func() { 498 It("fails with a useful error message", func() { 499 session := helpers.CF("login", "--sso-passcode", "some-passcode", "--sso") 500 Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: --sso-passcode, --sso")) 501 Eventually(session).Should(Exit(1)) 502 }) 503 }) 504 505 Context("and the provided passcode is incorrect", func() { 506 It("prompts twice, displays an error and fails", func() { 507 input := NewBuffer() 508 _, err := input.Write([]byte("bad-passcode-again\nbad-passcode-strikes-back\n")) 509 Expect(err).ToNot(HaveOccurred()) 510 session := helpers.CFWithStdin(input, "login", "--sso-passcode", "some-passcode") 511 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 512 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 513 Eventually(session.Err).Should(Say(`Invalid passcode`)) 514 515 // Leaving out expectation of prompt text, since it comes from UAA (and doesn't show up on Windows) 516 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 517 Eventually(session.Err).Should(Say(`Invalid passcode`)) 518 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 519 Eventually(session.Err).Should(Say(`Invalid passcode`)) 520 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 521 Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`)) 522 Eventually(session.Err).Should(Say(`Unable to authenticate`)) 523 Eventually(session).Should(Say(`FAILED`)) 524 525 Eventually(session).Should(Exit(1)) 526 }) 527 }) 528 529 When("a passcode isn't provided", func() { 530 It("prompts the user to try again", func() { 531 session := helpers.CF("login", "--sso-passcode") 532 Eventually(session.Err).Should(Say("Incorrect Usage: expected argument for flag `--sso-passcode'")) 533 Eventually(session).Should(Exit(1)) 534 }) 535 }) 536 }) 537 538 When("a user authenticates with valid client credentials", func() { 539 BeforeEach(func() { 540 clientID, clientSecret := helpers.SkipIfClientCredentialsNotSet() 541 session := helpers.CF("auth", clientID, clientSecret, "--client-credentials") 542 Eventually(session).Should(Exit(0)) 543 }) 544 545 When("a different user logs in with valid sso passcode", func() { 546 It("should fail log in and display an error informing the user they need to log out", func() { 547 session := helpers.CF("login", "--sso-passcode", "my-fancy-sso") 548 549 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 550 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 551 Eventually(session.Err).Should(Say(`Service account currently logged in\. Use 'cf logout' to log out service account and try again\.`)) 552 Eventually(session).Should(Say("FAILED")) 553 Eventually(session).Should(Exit(1)) 554 555 //And I am still logged in 556 targetSession := helpers.CF("target") 557 Eventually(targetSession).Should(Exit(0)) 558 }) 559 }) 560 }) 561 }) 562 563 Describe("Target Organization", func() { 564 var ( 565 orgName string 566 username string 567 password string 568 ) 569 570 BeforeEach(func() { 571 helpers.LoginCF() 572 orgName = helpers.NewOrgName() 573 session := helpers.CF("create-org", orgName) 574 Eventually(session).Should(Exit(0)) 575 username, password = helpers.CreateUserInOrgRole(orgName, "OrgManager") 576 }) 577 578 When("there is only one org available to the user", func() { 579 It("logs the user in and targets the organization automatically", func() { 580 session := helpers.CF("login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 581 Eventually(session).Should(Exit(0)) 582 583 targetSession := helpers.CF("target") 584 Eventually(targetSession).Should(Exit(0)) 585 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 586 }) 587 588 It("uses v3 endpoints", func() { 589 session := helpers.CF("login", "-v", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 590 Eventually(session).Should(Exit(0)) 591 592 Eventually(session).Should(Say(`GET /v3/organizations.*HTTP/`)) 593 Eventually(session).Should(Say(`GET /v3/spaces.*HTTP/`)) 594 }) 595 }) 596 597 When("the -o flag is not passed", func() { 598 When("there are multiple orgs available to the user", func() { 599 BeforeEach(func() { 600 orgName = helpers.NewOrgName() 601 createOrgSession := helpers.CF("create-org", orgName) 602 Eventually(createOrgSession).Should(Exit(0)) 603 setOrgRoleSession := helpers.CF("set-org-role", username, orgName, "OrgManager") 604 Eventually(setOrgRoleSession).Should(Exit(0)) 605 }) 606 607 When("there are more than 50 orgs", func() { 608 var server *ghttp.Server 609 610 BeforeEach(func() { 611 server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version) 612 helpers.AddLoginRoutes(server) 613 helpers.AddFiftyOneOrgs(server) 614 // handle request for spaces under "org20" 615 helpers.AddEmptyPaginatedResponse(server, "/v3/spaces?organization_guids=f6653aac-938e-4469-9a66-56a02796412b") 616 }) 617 618 AfterEach(func() { 619 server.Close() 620 }) 621 622 It("displays a message and prompts the user for the org name", func() { 623 input := NewBuffer() 624 _, wErr := input.Write([]byte(fmt.Sprintf("%s\n", "org20"))) // "org20" is one of the orgs in the test fixture 625 Expect(wErr).ToNot(HaveOccurred()) 626 627 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation") 628 629 Eventually(session).Should(Say("Select an org:")) 630 Eventually(session).Should(Say("There are too many options to display; please type in the name.")) 631 Eventually(session).Should(Say("\n\n")) 632 Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`))) 633 Eventually(session).Should(Say("Targeted org org20")) 634 635 Eventually(session).Should(Exit(0)) 636 }) 637 }) 638 639 When("user selects an organization by using numbered list", func() { 640 // required 641 It("prompts the user for org and targets the selected org", func() { 642 input := NewBuffer() 643 _, err := input.Write([]byte("1\n")) 644 Expect(err).ToNot(HaveOccurred()) 645 var session *Session 646 // TODO: do we still need this? 647 if skipSSLValidation { 648 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 649 } else { 650 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL) 651 } 652 653 Eventually(session).Should(Exit(0)) 654 655 re := regexp.MustCompile("1\\. (?P<OrgName>.*)\n") 656 matches := re.FindStringSubmatch(string(session.Out.Contents())) 657 Expect(matches).To(HaveLen((2))) 658 expectedOrgName := matches[1] 659 660 targetSession := helpers.CF("target") 661 Eventually(targetSession).Should(Exit(0)) 662 Eventually(targetSession).Should(Say(`org:\s+%s`, expectedOrgName)) 663 }) 664 665 When("the user selects a number greater than the number of orgs", func() { 666 // allowed to change 667 It("prompts the user until a valid number is entered", func() { 668 input := NewBuffer() 669 _, err := input.Write([]byte("3\n")) 670 Expect(err).ToNot(HaveOccurred()) 671 672 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password) 673 674 Eventually(session).Should(Say(regexp.QuoteMeta("Select an org:"))) 675 Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`))) 676 Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`))) 677 678 session.Interrupt() 679 Eventually(session).Should(Exit()) 680 }) 681 }) 682 }) 683 684 When("user selects an organization by org name", func() { 685 // required 686 It("prompts the user for an org and then targets the selected org", func() { 687 input := NewBuffer() 688 _, err := input.Write([]byte(fmt.Sprintf("%s\n", orgName))) 689 Expect(err).ToNot(HaveOccurred()) 690 691 var session *Session 692 if skipSSLValidation { 693 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 694 } else { 695 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL) 696 } 697 Eventually(session).Should(Say(`\d\. %s`, orgName)) 698 Eventually(session).Should(Exit(0)) 699 700 targetSession := helpers.CF("target") 701 Eventually(targetSession).Should(Exit(0)) 702 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 703 }) 704 }) 705 706 When("user does not select an organization", func() { 707 // allowed to change 708 It("succesfully logs in but does not target any org", func() { 709 input := NewBuffer() 710 _, err := input.Write([]byte("\n")) 711 Expect(err).ToNot(HaveOccurred()) 712 713 var session *Session 714 if skipSSLValidation { 715 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 716 } else { 717 session = helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL) 718 } 719 Eventually(session).Should(Say(`Org \(enter to skip\):`)) 720 Consistently(session).ShouldNot(Say(`Org \(enter to skip\):`)) 721 Eventually(session).Should(Exit(0)) 722 723 targetSession := helpers.CF("target") 724 Eventually(targetSession).Should(Exit(0)) 725 Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'")) 726 }) 727 }) 728 729 When("the user enters an invalid organization at the prompt", func() { 730 It("displays an error message and does not target the org", func() { 731 orgName = "invalid-org" 732 input := NewBuffer() 733 _, err := input.Write([]byte(fmt.Sprintf("%s\n", orgName))) 734 Expect(err).ToNot(HaveOccurred()) 735 736 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation") 737 Eventually(session).Should(Exit(1)) 738 Eventually(session).Should(Say("FAILED")) 739 Eventually(session.Err).Should(Say("Organization '%s' not found", orgName)) 740 741 targetSession := helpers.CF("target") 742 Eventually(targetSession).Should(Exit(0)) 743 Eventually(targetSession).Should(Say(`user:\s+%s`, username)) 744 Eventually(targetSession).ShouldNot(Say(`org:\s+%s`, orgName)) 745 Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'")) 746 }) 747 }) 748 }) 749 }) 750 751 When("the -o flag is passed", func() { 752 BeforeEach(func() { 753 helpers.LogoutCF() 754 }) 755 756 When("the organization is valid", func() { 757 It("targets the organization that was passed as an argument", func() { 758 session := helpers.CF("login", "-u", username, "-p", password, "-o", orgName) 759 760 Eventually(session).Should(Exit(0)) 761 Eventually(session).Should(Say(`Org:\s+%s`, orgName)) 762 763 targetSession := helpers.CF("target") 764 Eventually(targetSession).Should(Exit(0)) 765 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 766 }) 767 }) 768 769 When("the organization is invalid", func() { 770 It("logs in the user, displays an error message, and does not target any organization", func() { 771 orgName = "invalid-org" 772 session := helpers.CF("login", "-u", username, "-p", password, "-o", orgName) 773 774 Eventually(session).Should(Exit(1)) 775 Eventually(session).Should(Say("FAILED")) 776 Eventually(session.Err).Should(Say("Organization '%s' not found", orgName)) 777 778 targetSession := helpers.CF("target") 779 Eventually(targetSession).Should(Exit(0)) 780 Eventually(targetSession).Should(Say(`user:\s+%s`, username)) 781 Eventually(targetSession).ShouldNot(Say(`org:\s+%s`, orgName)) 782 Eventually(targetSession).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'")) 783 }) 784 }) 785 }) 786 }) 787 788 Describe("Target Space", func() { 789 var ( 790 orgName string 791 username string 792 password string 793 ) 794 795 BeforeEach(func() { 796 helpers.LoginCF() 797 orgName = helpers.NewOrgName() 798 session := helpers.CF("create-org", orgName) 799 Eventually(session).Should(Exit(0)) 800 username, password = helpers.CreateUser() 801 }) 802 803 When("only one space is available to the user", func() { 804 var spaceName string 805 806 BeforeEach(func() { 807 spaceName = helpers.NewSpaceName() 808 session := helpers.CF("create-space", "-o", orgName, spaceName) 809 Eventually(session).Should(Exit(0)) 810 roleSession := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceManager") 811 Eventually(roleSession).Should(Exit(0)) 812 }) 813 814 It("logs the user in and targets the org and the space", func() { 815 session := helpers.CF("login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 816 Eventually(session).Should(Exit(0)) 817 818 targetSession := helpers.CF("target") 819 Eventually(targetSession).Should(Exit(0)) 820 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 821 Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName)) 822 }) 823 824 When("the -s flag is passed", func() { 825 It("targets the org and the space", func() { 826 session := helpers.CF("login", "-u", username, "-p", password, "-a", apiURL, "-s", spaceName, "--skip-ssl-validation") 827 828 Eventually(session).Should(Say(`Targeted org\s+%s`, orgName)) 829 Eventually(session).Should(Say(`\n\nTargeted space\s+%s`, spaceName)) 830 831 Eventually(session).Should(Say(`Org:\s+%s`, orgName)) 832 Eventually(session).Should(Say(`Space:\s+%s`, spaceName)) 833 Eventually(session).Should(Exit(0)) 834 835 sessionOutput := string(session.Out.Contents()) 836 Expect(sessionOutput).To(MatchRegexp(`\S\n\n\n\nAPI`)) 837 838 targetSession := helpers.CF("target") 839 Eventually(targetSession).Should(Exit(0)) 840 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 841 Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName)) 842 }) 843 }) 844 }) 845 846 When("multiple spaces are available to the user", func() { 847 var ( 848 spaceName string 849 spaceName2 string 850 ) 851 852 BeforeEach(func() { 853 spaceName = helpers.NewSpaceName() 854 session := helpers.CF("create-space", "-o", orgName, spaceName) 855 Eventually(session).Should(Exit(0)) 856 roleSession := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceManager") 857 Eventually(roleSession).Should(Exit(0)) 858 859 spaceName2 = helpers.NewSpaceName() 860 session2 := helpers.CF("create-space", "-o", orgName, spaceName2) 861 Eventually(session2).Should(Exit(0)) 862 roleSession2 := helpers.CF("set-space-role", username, orgName, spaceName2, "SpaceManager") 863 Eventually(roleSession2).Should(Exit(0)) 864 }) 865 866 When("the -s flag is passed", func() { 867 BeforeEach(func() { 868 orgName2 := helpers.NewOrgName() 869 session := helpers.CF("create-org", orgName2) 870 Eventually(session).Should(Exit(0)) 871 session = helpers.CF("set-org-role", username, orgName2, "OrgManager") 872 Eventually(session).Should(Exit(0)) 873 }) 874 875 It("targets the org and the space", func() { 876 stdin := NewBuffer() 877 _, writeErr := stdin.Write([]byte(orgName + "\n")) 878 Expect(writeErr).ToNot(HaveOccurred()) 879 session := helpers.CFWithStdin(stdin, "login", "-u", username, "-p", password, "-a", apiURL, "-s", spaceName, "--skip-ssl-validation") 880 881 Eventually(session).Should(Say(`Targeted org\s+%s`, orgName)) 882 Eventually(session).Should(Say(`\n\nTargeted space\s+%s`, spaceName)) 883 884 Eventually(session).Should(Say(`Org:\s+%s`, orgName)) 885 Eventually(session).Should(Say(`Space:\s+%s`, spaceName)) 886 Eventually(session).Should(Exit(0)) 887 888 sessionOutput := string(session.Out.Contents()) 889 Expect(sessionOutput).To(MatchRegexp(`\S\n\n\n\nAPI`)) 890 891 targetSession := helpers.CF("target") 892 Eventually(targetSession).Should(Exit(0)) 893 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 894 Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName)) 895 }) 896 897 When("the space name is invalid", func() { 898 BeforeEach(func() { 899 spaceName = "invalid-space-name" 900 }) 901 902 It("the command fails and displays an error message. It targets the org but not the space.", func() { 903 stdin := NewBuffer() 904 _, writeErr := stdin.Write([]byte(orgName + "\n")) 905 Expect(writeErr).ToNot(HaveOccurred()) 906 session := helpers.CFWithStdin(stdin, "login", "-u", username, "-p", password, "-a", apiURL, "-s", spaceName, "--skip-ssl-validation") 907 Eventually(session).Should(Exit(1)) 908 Eventually(session).Should(Say("FAILED")) 909 Eventually(session.Err).Should(Say("Space '%s' not found", spaceName)) 910 911 targetSession := helpers.CF("target") 912 Eventually(targetSession).Should(Exit(0)) 913 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 914 Eventually(targetSession).ShouldNot(Say(`space:\s+%s`, spaceName)) 915 Eventually(targetSession).Should(Say("No space targeted, use 'cf target -s SPACE'")) 916 }) 917 }) 918 }) 919 920 When("the -s flag is not passed", func() { 921 It("prompts the user to pick their space by list position", func() { 922 input := NewBuffer() 923 _, err := input.Write([]byte("1\n")) 924 Expect(err).ToNot(HaveOccurred()) 925 926 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 927 Eventually(session).Should(Exit(0)) 928 929 re := regexp.MustCompile("1\\. (?P<SpaceName>.*)\n") 930 submatches := re.FindStringSubmatch(string(session.Out.Contents())) 931 Expect(submatches).ToNot(BeEmpty(), "missing numbered space list") 932 expectedSpaceName := submatches[1] 933 934 targetSession := helpers.CF("target") 935 Eventually(targetSession).Should(Exit(0)) 936 Eventually(targetSession).Should(Say(`space:\s+%s`, expectedSpaceName)) 937 }) 938 939 It("reprompts the user if an invalid number is entered", func() { 940 input := NewBuffer() 941 _, err := input.Write([]byte("4\n")) 942 Expect(err).ToNot(HaveOccurred()) 943 944 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 945 Eventually(session).Should(Say(regexp.QuoteMeta("Space (enter to skip):"))) 946 Eventually(session).Should(Say(regexp.QuoteMeta("Space (enter to skip):"))) 947 session.Interrupt() 948 Eventually(session).Should(Exit()) 949 }) 950 951 It("prompts the user to pick their space by name", func() { 952 input := NewBuffer() 953 _, err := input.Write([]byte(spaceName + "\n")) 954 Expect(err).ToNot(HaveOccurred()) 955 956 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 957 Eventually(session).Should(Exit(0)) 958 959 targetSession := helpers.CF("target") 960 Eventually(targetSession).Should(Exit(0)) 961 Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName)) 962 }) 963 964 It("allows the user to skip picking a space", func() { 965 input := NewBuffer() 966 _, err := input.Write([]byte("\n")) 967 Expect(err).ToNot(HaveOccurred()) 968 969 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 970 Eventually(session).Should(Exit(0)) 971 972 targetSession := helpers.CF("target") 973 Eventually(targetSession).Should(Exit(0)) 974 Eventually(targetSession).Should(Say(`No space targeted, use 'cf target -s SPACE'`)) 975 }) 976 977 When("the input space name is invalid", func() { 978 BeforeEach(func() { 979 spaceName = "invalid-space-name" 980 }) 981 982 It("the command fails and displays an error message. It does not target the space.", func() { 983 input := NewBuffer() 984 _, err := input.Write([]byte(spaceName + "\n")) 985 Expect(err).ToNot(HaveOccurred()) 986 987 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "-a", apiURL, "--skip-ssl-validation") 988 Eventually(session).Should(Exit(1)) 989 Eventually(session).Should(Say("FAILED")) 990 Eventually(session.Err).Should(Say("Space '%s' not found", spaceName)) 991 992 targetSession := helpers.CF("target") 993 Eventually(targetSession).Should(Exit(0)) 994 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName)) 995 Eventually(targetSession).ShouldNot(Say(`space:\s+%s`, spaceName)) 996 Eventually(targetSession).Should(Say("No space targeted, use 'cf target -s SPACE'")) 997 }) 998 }) 999 1000 When("there are more than 50 spaces", func() { 1001 var server *ghttp.Server 1002 BeforeEach(func() { 1003 server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version) 1004 helpers.AddLoginRoutes(server) 1005 helpers.AddFiftyOneSpaces(server) 1006 }) 1007 1008 AfterEach(func() { 1009 server.Close() 1010 }) 1011 1012 It("displays a message and prompts the user for the space name", func() { 1013 input := NewBuffer() 1014 _, wErr := input.Write([]byte(fmt.Sprintf("%s\n", "test-space-1"))) 1015 Expect(wErr).ToNot(HaveOccurred()) 1016 1017 session := helpers.CFWithStdin(input, "login", "-u", username, "-p", password, "--skip-ssl-validation") 1018 1019 Eventually(session).Should(Say("Select a space:")) 1020 Eventually(session).Should(Say("There are too many options to display; please type in the name.")) 1021 Eventually(session).Should(Say("\n\n")) 1022 Eventually(session).Should(Say(regexp.QuoteMeta(`Space (enter to skip):`))) 1023 Eventually(session).Should(Say("Targeted space test-space-1")) 1024 1025 Eventually(session).Should(Exit(0)) 1026 }) 1027 }) 1028 }) 1029 }) 1030 1031 }) 1032 1033 Describe("Full interactive happy path", func() { 1034 var ( 1035 orgName1 string 1036 orgName2 string 1037 spaceName1 string 1038 spaceName2 string 1039 username string 1040 password string 1041 ) 1042 1043 BeforeEach(func() { 1044 helpers.LoginCF() 1045 orgName1 = helpers.NewOrgName() 1046 orgName2 = helpers.NewOrgName() 1047 spaceName1 = helpers.NewSpaceName() 1048 spaceName2 = helpers.NewSpaceName() 1049 1050 Eventually(helpers.CF("create-org", orgName1)).Should(Exit(0)) 1051 Eventually(helpers.CF("create-org", orgName2)).Should(Exit(0)) 1052 Eventually(helpers.CF("create-space", "-o", orgName1, spaceName1)).Should(Exit(0)) 1053 Eventually(helpers.CF("create-space", "-o", orgName1, spaceName2)).Should(Exit(0)) 1054 1055 username, password = helpers.CreateUser() 1056 Eventually(helpers.CF("set-org-role", username, orgName1, "OrgManager")).Should(Exit(0)) 1057 Eventually(helpers.CF("set-org-role", username, orgName2, "OrgManager")).Should(Exit(0)) 1058 Eventually(helpers.CF("set-space-role", username, orgName1, spaceName1, "SpaceManager")).Should(Exit(0)) 1059 Eventually(helpers.CF("set-space-role", username, orgName1, spaceName2, "SpaceManager")).Should(Exit(0)) 1060 }) 1061 1062 When("there are multiple orgs and spaces available to a user", func() { 1063 It("prompts for username, password, org, and space. Then logs in and targets correctly", func() { 1064 buffer := NewBuffer() 1065 _, err := buffer.Write([]byte(fmt.Sprintf("%s\n%s\n%s\n%s\n", username, password, orgName1, spaceName2))) 1066 Expect(err).ToNot(HaveOccurred()) 1067 1068 session := helpers.CFWithStdin(buffer, "login") 1069 Eventually(session).Should(Say("Email:")) 1070 Eventually(session).Should(Say("\n\n")) 1071 Eventually(session).Should(Say("Password:")) 1072 Eventually(session).Should(Say("OK")) 1073 Eventually(session).Should(Say("\n\n")) 1074 Eventually(session).Should(Say("Select an org:")) 1075 Eventually(session).Should(Say("\n\n")) 1076 Eventually(session).Should(Say(regexp.QuoteMeta(`Org (enter to skip):`))) 1077 Eventually(session).Should(Say(fmt.Sprintf("Targeted org %s", orgName1))) 1078 Eventually(session).Should(Say("\n\n")) 1079 Eventually(session).Should(Say("Select a space:")) 1080 Eventually(session).Should(Say("\n\n")) 1081 Eventually(session).Should(Say(regexp.QuoteMeta(`Space (enter to skip):`))) 1082 Eventually(session).Should(Say(fmt.Sprintf("Targeted space %s", spaceName2))) 1083 Eventually(session).Should(Say("\n\n")) 1084 Eventually(session).Should(Say(`Org:\s+%s`, orgName1)) 1085 Eventually(session).Should(Say(`Space:\s+%s`, spaceName2)) 1086 Eventually(session).Should(Exit(0)) 1087 1088 targetSession := helpers.CF("target") 1089 Eventually(targetSession).Should(Exit(0)) 1090 Eventually(targetSession).Should(Say(`org:\s+%s`, orgName1)) 1091 Eventually(targetSession).Should(Say(`space:\s+%s`, spaceName2)) 1092 }) 1093 }) 1094 }) 1095 1096 Describe("User Credentials", func() { 1097 It("prompts the user for email and password", func() { 1098 username, password := helpers.GetCredentials() 1099 buffer := NewBuffer() 1100 _, err := buffer.Write([]byte(fmt.Sprintf("%s\n%s\n", username, password))) 1101 Expect(err).ToNot(HaveOccurred()) 1102 session := helpers.CFWithStdin(buffer, "login") 1103 Eventually(session).Should(Say("Email:")) 1104 Eventually(session).Should(Say("\n\n")) 1105 Eventually(session).Should(Say("Password:")) 1106 Eventually(session).Should(Say("\n\n")) 1107 Eventually(session).Should(Exit(0)) 1108 }) 1109 1110 When("the user's account has been locked due to too many failed attempts", func() { 1111 var username string 1112 1113 BeforeEach(func() { 1114 helpers.LoginCF() 1115 username, _ = helpers.CreateUser() 1116 helpers.LogoutCF() 1117 }) 1118 1119 It("displays a helpful error and does not reprompt", func() { 1120 input := NewBuffer() 1121 _, err := input.Write([]byte("garbage\ngarbage\ngarbage\n")) 1122 Expect(err).ToNot(HaveOccurred()) 1123 session := helpers.CFWithStdin(input, "login", "-u", username) 1124 Eventually(session).Should(Exit(1)) 1125 1126 input = NewBuffer() 1127 _, err = input.Write([]byte("garbage\ngarbage\ngarbage\n")) 1128 Expect(err).ToNot(HaveOccurred()) 1129 session = helpers.CFWithStdin(input, "login", "-u", username) 1130 Eventually(session).Should(Exit(1)) 1131 1132 input = NewBuffer() 1133 _, err = input.Write([]byte("garbage\ngarbage\ngarbage\n")) 1134 Expect(err).NotTo(HaveOccurred()) 1135 session = helpers.CFWithStdin(input, "login", "-u", username) 1136 Eventually(session).Should(Say(`Password`)) 1137 Eventually(session.Err).Should(Say(`Your account has been locked because of too many failed attempts to login\.`)) 1138 Consistently(session).ShouldNot(Say(`Password`)) 1139 Eventually(session.Err).Should(Say(`Unable to authenticate.`)) 1140 Eventually(session).Should(Say("FAILED")) 1141 Eventually(session).Should(Exit(1)) 1142 }) 1143 }) 1144 1145 When("the -u flag is provided", func() { 1146 It("prompts the user for their password", func() { 1147 username, password := helpers.GetCredentials() 1148 buffer := NewBuffer() 1149 _, err := buffer.Write([]byte(fmt.Sprintf("%s\n", password))) 1150 Expect(err).ToNot(HaveOccurred()) 1151 session := helpers.CFWithStdin(buffer, "login", "-u", username) 1152 Eventually(session).Should(Say("Password: ")) 1153 Eventually(session).Should(Exit(0)) 1154 }) 1155 }) 1156 1157 When("the user provides the -p flag", func() { 1158 It("prompts the user for their email and logs in successfully", func() { 1159 username, password := helpers.GetCredentials() 1160 input := NewBuffer() 1161 _, err := input.Write([]byte(username + "\n")) 1162 Expect(err).ToNot(HaveOccurred()) 1163 session := helpers.CFWithStdin(input, "login", "-p", password) 1164 Eventually(session).Should(Say("Email: ")) 1165 Eventually(session).Should(Exit(0)) 1166 }) 1167 1168 When("the password flag is given incorrectly", func() { 1169 It("Prompts the user two more times before exiting with an error", func() { 1170 username, _ := helpers.GetCredentials() 1171 input := NewBuffer() 1172 _, err := input.Write([]byte(username + "\n" + "bad-password\n" + "bad-password\n")) 1173 Expect(err).ToNot(HaveOccurred()) 1174 session := helpers.CFWithStdin(input, "login", "-p", "bad-password") 1175 Eventually(session).Should(Say("Email: ")) 1176 Eventually(session.Err).Should(Say("Credentials were rejected, please try again.")) 1177 Eventually(session).Should(Say("Password: ")) 1178 Eventually(session.Err).Should(Say("Credentials were rejected, please try again.")) 1179 Eventually(session).Should(Say("Not logged in. Use 'cf login' or 'cf login --sso' to log in.")) 1180 Eventually(session).Should(Say("FAILED")) 1181 Eventually(session.Err).Should(Say("Unable to authenticate.")) 1182 Eventually(session).Should(Exit(1)) 1183 }) 1184 }) 1185 }) 1186 1187 When("multiple interactive prompts are used", func() { 1188 var ( 1189 orgName string 1190 orgName2 string 1191 username string 1192 password string 1193 ) 1194 1195 BeforeEach(func() { 1196 helpers.LoginCF() 1197 orgName = helpers.NewOrgName() 1198 session := helpers.CF("create-org", orgName) 1199 Eventually(session).Should(Exit(0)) 1200 username, password = helpers.CreateUserInOrgRole(orgName, "OrgManager") 1201 1202 orgName2 = helpers.NewOrgName() 1203 Eventually(helpers.CF("create-org", orgName2)).Should(Exit(0)) 1204 setOrgRoleSession := helpers.CF("set-org-role", username, orgName2, "OrgManager") 1205 Eventually(setOrgRoleSession).Should(Exit(0)) 1206 }) 1207 1208 It("should accept each value", func() { 1209 input := NewBuffer() 1210 _, err := input.Write([]byte(username + "\n" + password + "\n" + orgName + "\n")) 1211 Expect(err).ToNot(HaveOccurred()) 1212 session := helpers.CFWithStdin(input, "login") 1213 Eventually(session).Should(Exit(0)) 1214 }) 1215 1216 When("MFA is enabled", func() { 1217 var ( 1218 password string 1219 mfaCode string 1220 server *ghttp.Server 1221 ) 1222 1223 BeforeEach(func() { 1224 password = "some-password" 1225 mfaCode = "123456" 1226 server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version) 1227 helpers.AddMfa(server, password, mfaCode) 1228 }) 1229 1230 AfterEach(func() { 1231 server.Close() 1232 }) 1233 1234 When("correct MFA code and credentials are provided", func() { 1235 BeforeEach(func() { 1236 fakeTokenResponse := map[string]string{ 1237 "access_token": "", 1238 "token_type": "bearer", 1239 } 1240 server.RouteToHandler(http.MethodPost, "/oauth/token", 1241 ghttp.RespondWithJSONEncoded(http.StatusOK, fakeTokenResponse)) 1242 server.RouteToHandler(http.MethodGet, "/v3/organizations", 1243 ghttp.RespondWith(http.StatusOK, `{ 1244 "total_results": 0, 1245 "total_pages": 1, 1246 "resources": []}`)) 1247 }) 1248 1249 It("logs in the user", func() { 1250 input := NewBuffer() 1251 _, err := input.Write([]byte(username + "\n" + password + "\n" + mfaCode + "\n")) 1252 Expect(err).ToNot(HaveOccurred()) 1253 session := helpers.CFWithStdin(input, "login") 1254 Eventually(session).Should(Say("Email: ")) 1255 Eventually(session).Should(Say("\n\n")) 1256 Eventually(session).Should(Say("Password:")) 1257 Eventually(session).Should(Say("\n\n")) 1258 Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL())) 1259 Eventually(session).Should(Exit(0)) 1260 }) 1261 }) 1262 1263 When("incorrect MFA code and credentials are provided", func() { 1264 It("fails", func() { 1265 input := NewBuffer() 1266 wrongMfaCode := mfaCode + "foo" 1267 _, err := input.Write([]byte(username + "\n" + password + "\n" + wrongMfaCode + "\n" + password + "\n" + wrongMfaCode + "\n")) 1268 Expect(err).ToNot(HaveOccurred()) 1269 session := helpers.CFWithStdin(input, "login") 1270 Eventually(session).Should(Say("Password: ")) 1271 Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL())) 1272 Eventually(session).Should(Say("Password: ")) 1273 Eventually(session).Should(Say("MFA Code \\( Register at %[1]s \\)", server.URL())) 1274 Eventually(session).Should(Say("Not logged in. Use 'cf login' or 'cf login --sso' to log in.")) 1275 Eventually(session).Should(Say("FAILED")) 1276 Eventually(session.Err).Should(Say("Unable to authenticate.")) 1277 1278 Eventually(session).Should(Exit(1)) 1279 }) 1280 }) 1281 }) 1282 }) 1283 1284 When("the user provides the -p and -u flags", func() { 1285 Context("and the credentials are correct", func() { 1286 It("logs in successfully", func() { 1287 username, password := helpers.GetCredentials() 1288 session := helpers.CF("login", "-p", password, "-u", username) 1289 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 1290 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 1291 Eventually(session).Should(Say(`OK`)) 1292 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 1293 Eventually(session).Should(Say("User:\\s+" + username)) 1294 Eventually(session).Should(Exit(0)) 1295 }) 1296 }) 1297 1298 Context("and the credentials are incorrect", func() { 1299 It("prompts twice, displays an error and fails", func() { 1300 input := NewBuffer() 1301 _, err := input.Write([]byte("garbage\ngarbage\n")) 1302 Expect(err).ToNot(HaveOccurred()) 1303 session := helpers.CFWithStdin(input, "login", "-p", "nope", "-u", "faker") 1304 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 1305 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 1306 Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`)) 1307 Eventually(session).Should(Say(`Password:`)) 1308 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 1309 Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`)) 1310 Eventually(session).Should(Say(`Password:`)) 1311 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 1312 Eventually(session.Err).Should(Say(`Credentials were rejected, please try again.`)) 1313 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 1314 Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`)) 1315 Eventually(session.Err).Should(Say(`Unable to authenticate.`)) 1316 Eventually(session).Should(Say(`FAILED`)) 1317 1318 Eventually(session).Should(Exit(1)) 1319 }) 1320 1321 Context("and the user was previously logged in", func() { 1322 BeforeEach(func() { 1323 helpers.LoginCF() 1324 }) 1325 1326 It("logs them out", func() { 1327 session := helpers.CF("login", "-p", "nope", "-u", "faker") 1328 Eventually(session).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`)) 1329 Eventually(session).Should(Exit()) 1330 1331 orgsSession := helpers.CF("orgs") 1332 Eventually(orgsSession.Err).Should(Say(`Not logged in. Use 'cf login' or 'cf login --sso' to log in.`)) 1333 Eventually(orgsSession).Should(Exit()) 1334 }) 1335 }) 1336 }) 1337 1338 When("already logged in with client credentials", func() { 1339 BeforeEach(func() { 1340 clientID, clientSecret := helpers.SkipIfClientCredentialsNotSet() 1341 session := helpers.CF("auth", clientID, clientSecret, "--client-credentials") 1342 Eventually(session).Should(Exit(0)) 1343 }) 1344 1345 It("should fail log in and display an error informing the user they need to log out", func() { 1346 username, password := helpers.GetCredentials() 1347 session := helpers.CF("login", "-p", password, "-u", username) 1348 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 1349 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 1350 Eventually(session).Should(Say("FAILED")) 1351 Eventually(session.Err).Should(Say("Service account currently logged in. Use 'cf logout' to log out service account and try again.")) 1352 Eventually(session).Should(Exit(1)) 1353 }) 1354 }) 1355 }) 1356 }) 1357 1358 Describe("Authenticating as a user, through a custom client", func() { 1359 var ( 1360 accessTokenExpiration time.Duration 1361 username string 1362 password string 1363 customClientID string 1364 customClientSecret string 1365 ) 1366 1367 BeforeEach(func() { 1368 customClientID, customClientSecret = helpers.SkipIfCustomClientCredentialsNotSet() 1369 1370 helpers.LoginCF() 1371 username, password = helpers.CreateUser() 1372 1373 helpers.SetConfig(func(config *configv3.Config) { 1374 config.ConfigFile.UAAOAuthClient = customClientID 1375 config.ConfigFile.UAAOAuthClientSecret = customClientSecret 1376 config.ConfigFile.UAAGrantType = "" 1377 }) 1378 1379 session := helpers.CF("login", "-u", username, "-p", password) 1380 Eventually(session).Should(Exit(0)) 1381 }) 1382 1383 It("gets a token whose settings match those of the custom client", func() { 1384 accessTokenExpiration = 120 // this was configured in the pipeline 1385 1386 config := helpers.GetConfig() 1387 1388 jwt := helpers.ParseTokenString(config.ConfigFile.AccessToken) 1389 expires, expIsSet := jwt.Claims().Expiration() 1390 Expect(expIsSet).To(BeTrue()) 1391 1392 iat, iatIsSet := jwt.Claims().IssuedAt() 1393 1394 Expect(iatIsSet).To(BeTrue()) 1395 Expect(expires.Sub(iat)).To(Equal(accessTokenExpiration * time.Second)) 1396 }) 1397 1398 It("warns the user that this configuration is deprecated", func() { 1399 deprecationMessage := "Deprecation warning: Manually writing your client credentials to the config.json is deprecated and will be removed in the future. For similar functionality, please use the `cf auth --client-credentials` command instead." 1400 1401 session := helpers.CF("login", "-u", username, "-p", password) 1402 Eventually(session.Err).Should(Say(deprecationMessage)) 1403 Eventually(session).Should(Exit(0)) 1404 }) 1405 1406 When("the token has expired", func() { 1407 BeforeEach(func() { 1408 helpers.SetConfig(func(config *configv3.Config) { 1409 config.ConfigFile.AccessToken = helpers.ExpiredAccessToken() 1410 }) 1411 }) 1412 1413 It("re-authenticates using the custom client", func() { 1414 session := helpers.CF("orgs") 1415 Eventually(session).Should(Exit(0)) 1416 }) 1417 }) 1418 }) 1419 1420 Describe("Setting the identity provider to be used", func() { 1421 When("the user provides the --origin flag", func() { 1422 It("logs in successfully", func() { 1423 username, password := helpers.GetCredentials() 1424 session := helpers.CF("login", "-u", username, "-p", password, "--origin", "uaa") 1425 Eventually(session).Should(Say("API endpoint:\\s+" + helpers.GetAPI())) 1426 Eventually(session).Should(Say(`Authenticating\.\.\.`)) 1427 Eventually(session).Should(Say(`OK`)) 1428 Eventually(session).Should(Say(`API endpoint:\s+` + helpers.GetAPI() + `\s+\(API version: \d\.\d{1,3}\.\d{1,3}\)`)) 1429 Eventually(session).Should(Say("User:\\s+" + username)) 1430 Eventually(session).Should(Exit(0)) 1431 }) 1432 }) 1433 1434 When("the user provides the --origin flag and --sso flag", func() { 1435 It("rejects the command", func() { 1436 username, password := helpers.GetCredentials() 1437 session := helpers.CF("login", "-u", username, "-p", password, "--sso", "--origin", "uaa") 1438 1439 Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --sso, --origin`)) 1440 1441 Eventually(session).Should(Exit(1)) 1442 }) 1443 }) 1444 When("the user provides the --origin flag and --sso-passcode flag", func() { 1445 It("rejects the command", func() { 1446 username, password := helpers.GetCredentials() 1447 session := helpers.CF("login", "-u", username, "-p", password, "--sso-passcode", "some-passcode", "--origin", "uaa") 1448 1449 Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --sso-passcode, --origin`)) 1450 1451 Eventually(session).Should(Exit(1)) 1452 }) 1453 }) 1454 }) 1455 })