github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/integration/shared/isolated/login_command_test.go (about)

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