github.com/arunkumar7540/cli@v6.45.0+incompatible/integration/shared/isolated/login_command_test.go (about)

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