github.com/swisscom/cloudfoundry-cli@v7.1.0+incompatible/cf/commands/login_test.go (about) 1 package commands_test 2 3 import ( 4 "strconv" 5 6 "code.cloudfoundry.org/cli/cf/api/authentication/authenticationfakes" 7 "code.cloudfoundry.org/cli/cf/api/organizations/organizationsfakes" 8 "code.cloudfoundry.org/cli/cf/api/spaces/spacesfakes" 9 "code.cloudfoundry.org/cli/cf/commandregistry" 10 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 11 "code.cloudfoundry.org/cli/cf/configuration/coreconfig/coreconfigfakes" 12 "code.cloudfoundry.org/cli/cf/errors" 13 "code.cloudfoundry.org/cli/cf/models" 14 testcmd "code.cloudfoundry.org/cli/cf/util/testhelpers/commands" 15 testconfig "code.cloudfoundry.org/cli/cf/util/testhelpers/configuration" 16 testterm "code.cloudfoundry.org/cli/cf/util/testhelpers/terminal" 17 . "github.com/onsi/ginkgo" 18 . "github.com/onsi/gomega" 19 20 . "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers" 21 ) 22 23 var _ = Describe("Login Command", func() { 24 var ( 25 Flags []string 26 Config coreconfig.Repository 27 ui *testterm.FakeUI 28 authRepo *authenticationfakes.FakeRepository 29 endpointRepo *coreconfigfakes.FakeEndpointRepository 30 orgRepo *organizationsfakes.FakeOrganizationRepository 31 spaceRepo *spacesfakes.FakeSpaceRepository 32 33 org models.Organization 34 deps commandregistry.Dependency 35 36 minCLIVersion string 37 minRecommendedCLIVersion string 38 apiVersion string 39 ) 40 41 updateCommandDependency := func(pluginCall bool) { 42 deps.UI = ui 43 deps.Config = Config 44 deps.RepoLocator = deps.RepoLocator.SetEndpointRepository(endpointRepo) 45 deps.RepoLocator = deps.RepoLocator.SetAuthenticationRepository(authRepo) 46 deps.RepoLocator = deps.RepoLocator.SetOrganizationRepository(orgRepo) 47 deps.RepoLocator = deps.RepoLocator.SetSpaceRepository(spaceRepo) 48 commandregistry.Commands.SetCommand(commandregistry.Commands.FindCommand("login").SetDependency(deps, pluginCall)) 49 } 50 51 listSpacesStub := func(spaces []models.Space) func(func(models.Space) bool) error { 52 return func(cb func(models.Space) bool) error { 53 var keepGoing bool 54 for _, s := range spaces { 55 keepGoing = cb(s) 56 if !keepGoing { 57 return nil 58 } 59 } 60 return nil 61 } 62 } 63 64 BeforeEach(func() { 65 Flags = []string{} 66 Config = testconfig.NewRepository() 67 ui = &testterm.FakeUI{} 68 authRepo = new(authenticationfakes.FakeRepository) 69 authRepo.AuthenticateStub = func(credentials map[string]string) error { 70 Config.SetAccessToken("my_access_token") 71 Config.SetRefreshToken("my_refresh_token") 72 return nil 73 } 74 endpointRepo = new(coreconfigfakes.FakeEndpointRepository) 75 minCLIVersion = "1.0.0" 76 minRecommendedCLIVersion = "1.0.0" 77 apiVersion = "100.200.300" 78 79 org = models.Organization{} 80 org.Name = "my-new-org" 81 org.GUID = "my-new-org-guid" 82 83 orgRepo = &organizationsfakes.FakeOrganizationRepository{} 84 orgRepo.ListOrgsReturns([]models.Organization{org}, nil) 85 86 space := models.Space{} 87 space.GUID = "my-space-guid" 88 space.Name = "my-space" 89 90 spaceRepo = new(spacesfakes.FakeSpaceRepository) 91 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{space}) 92 93 authRepo.GetLoginPromptsAndSaveUAAServerURLReturns(map[string]coreconfig.AuthPrompt{ 94 "username": { 95 DisplayName: "Username", 96 Type: coreconfig.AuthPromptTypeText, 97 }, 98 "password": { 99 DisplayName: "Password", 100 Type: coreconfig.AuthPromptTypePassword, 101 }, 102 }, nil) 103 }) 104 105 Context("interactive usage", func() { 106 JustBeforeEach(func() { 107 endpointRepo.GetCCInfoStub = func(endpoint string) (*coreconfig.CCInfo, string, error) { 108 return &coreconfig.CCInfo{ 109 APIVersion: apiVersion, 110 AuthorizationEndpoint: "auth/endpoint", 111 DopplerEndpoint: "doppler/endpoint", 112 MinCLIVersion: minCLIVersion, 113 MinRecommendedCLIVersion: minRecommendedCLIVersion, 114 SSHOAuthClient: "some-client", 115 RoutingAPIEndpoint: "routing/endpoint", 116 }, endpoint, nil 117 } 118 }) 119 120 Describe("when there are a small number of organizations and spaces", func() { 121 var org2 models.Organization 122 var space2 models.Space 123 124 BeforeEach(func() { 125 org1 := models.Organization{} 126 org1.GUID = "some-org-guid" 127 org1.Name = "some-org" 128 129 org2 = models.Organization{} 130 org2.GUID = "my-new-org-guid" 131 org2.Name = "my-new-org" 132 133 space1 := models.Space{} 134 space1.GUID = "my-space-guid" 135 space1.Name = "my-space" 136 137 space2 = models.Space{} 138 space2.GUID = "some-space-guid" 139 space2.Name = "some-space" 140 141 orgRepo.ListOrgsReturns([]models.Organization{org1, org2}, nil) 142 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{space1, space2}) 143 spaceRepo.FindByNameStub = func(name string) (models.Space, error) { 144 m := map[string]models.Space{ 145 space1.Name: space1, 146 space2.Name: space2, 147 } 148 return m[name], nil 149 } 150 }) 151 152 It("lets the user select an org and space by number", func() { 153 orgRepo.FindByNameReturns(org2, nil) 154 OUT_OF_RANGE_CHOICE := "3" 155 ui.Inputs = []string{"api.example.com", "user@example.com", "password", OUT_OF_RANGE_CHOICE, "2", OUT_OF_RANGE_CHOICE, "1"} 156 157 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 158 159 Expect(ui.Outputs()).To(ContainSubstrings( 160 []string{"Select an org"}, 161 []string{"1. some-org"}, 162 []string{"2. my-new-org"}, 163 []string{"Select a space"}, 164 []string{"1. my-space"}, 165 []string{"2. some-space"}, 166 )) 167 168 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 169 Expect(Config.SpaceFields().GUID).To(Equal("my-space-guid")) 170 Expect(Config.AccessToken()).To(Equal("my_access_token")) 171 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 172 173 Expect(Config.APIEndpoint()).To(Equal("api.example.com")) 174 Expect(Config.APIVersion()).To(Equal("100.200.300")) 175 Expect(Config.AuthenticationEndpoint()).To(Equal("auth/endpoint")) 176 Expect(Config.SSHOAuthClient()).To(Equal("some-client")) 177 Expect(Config.MinCLIVersion()).To(Equal("1.0.0")) 178 Expect(Config.MinRecommendedCLIVersion()).To(Equal("1.0.0")) 179 Expect(Config.DopplerEndpoint()).To(Equal("doppler/endpoint")) 180 Expect(Config.RoutingAPIEndpoint()).To(Equal("routing/endpoint")) 181 182 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 183 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("api.example.com")) 184 185 Expect(orgRepo.FindByNameArgsForCall(0)).To(Equal("my-new-org")) 186 Expect(spaceRepo.FindByNameArgsForCall(0)).To(Equal("my-space")) 187 188 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 189 }) 190 191 It("lets the user select an org and space by name", func() { 192 ui.Inputs = []string{"api.example.com", "user@example.com", "password", "my-new-org", "my-space"} 193 orgRepo.FindByNameReturns(org2, nil) 194 195 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 196 197 Expect(ui.Outputs()).To(ContainSubstrings( 198 []string{"Select an org"}, 199 []string{"1. some-org"}, 200 []string{"2. my-new-org"}, 201 []string{"Select a space"}, 202 []string{"1. my-space"}, 203 []string{"2. some-space"}, 204 )) 205 206 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 207 Expect(Config.SpaceFields().GUID).To(Equal("my-space-guid")) 208 Expect(Config.AccessToken()).To(Equal("my_access_token")) 209 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 210 211 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 212 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("api.example.com")) 213 214 Expect(orgRepo.FindByNameArgsForCall(0)).To(Equal("my-new-org")) 215 Expect(spaceRepo.FindByNameArgsForCall(0)).To(Equal("my-space")) 216 217 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 218 }) 219 220 It("lets the user specify an org and space using flags", func() { 221 Flags = []string{"-a", "api.example.com", "-u", "user@example.com", "-p", "password", "-o", "my-new-org", "-s", "my-space"} 222 223 orgRepo.FindByNameReturns(org2, nil) 224 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 225 226 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 227 Expect(Config.SpaceFields().GUID).To(Equal("my-space-guid")) 228 Expect(Config.AccessToken()).To(Equal("my_access_token")) 229 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 230 231 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 232 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("api.example.com")) 233 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 234 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 235 "username": "user@example.com", 236 "password": "password", 237 })) 238 239 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 240 }) 241 242 It("doesn't ask the user for the API url if they have it in their config", func() { 243 orgRepo.FindByNameReturns(org, nil) 244 Config.SetAPIEndpoint("http://api.example.com") 245 246 Flags = []string{"-o", "my-new-org", "-s", "my-space"} 247 ui.Inputs = []string{"user@example.com", "password"} 248 249 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 250 251 Expect(Config.APIEndpoint()).To(Equal("http://api.example.com")) 252 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 253 Expect(Config.SpaceFields().GUID).To(Equal("my-space-guid")) 254 Expect(Config.AccessToken()).To(Equal("my_access_token")) 255 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 256 257 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 258 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("http://api.example.com")) 259 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 260 }) 261 }) 262 263 It("displays an update notification", func() { 264 ui.Inputs = []string{"http://api.example.com", "user@example.com", "password"} 265 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 266 Expect(ui.NotifyUpdateIfNeededCallCount).To(Equal(1)) 267 }) 268 269 It("tries to get the organizations", func() { 270 Flags = []string{} 271 ui.Inputs = []string{"api.example.com", "user@example.com", "password", "my-org-1", "my-space"} 272 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 273 Expect(orgRepo.ListOrgsCallCount()).To(Equal(1)) 274 Expect(orgRepo.ListOrgsArgsForCall(0)).To(Equal(50)) 275 }) 276 277 Describe("when there are too many orgs to show", func() { 278 BeforeEach(func() { 279 organizations := []models.Organization{} 280 for i := 0; i < 60; i++ { 281 id := strconv.Itoa(i) 282 org := models.Organization{} 283 org.GUID = "my-org-guid-" + id 284 org.Name = "my-org-" + id 285 organizations = append(organizations, org) 286 } 287 orgRepo.ListOrgsReturns(organizations, nil) 288 orgRepo.FindByNameReturns(organizations[1], nil) 289 290 space1 := models.Space{} 291 space1.GUID = "my-space-guid" 292 space1.Name = "my-space" 293 294 space2 := models.Space{} 295 space2.GUID = "some-space-guid" 296 space2.Name = "some-space" 297 298 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{space1, space2}) 299 }) 300 301 It("doesn't display a list of orgs (the user must type the name)", func() { 302 ui.Inputs = []string{"api.example.com", "user@example.com", "password", "my-org-1", "my-space"} 303 304 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 305 306 Expect(ui.Outputs()).ToNot(ContainSubstrings([]string{"my-org-2"})) 307 Expect(orgRepo.FindByNameArgsForCall(0)).To(Equal("my-org-1")) 308 Expect(Config.OrganizationFields().GUID).To(Equal("my-org-guid-1")) 309 }) 310 }) 311 312 Describe("when there is only a single org and space", func() { 313 It("does not ask the user to select an org/space", func() { 314 ui.Inputs = []string{"http://api.example.com", "user@example.com", "password"} 315 316 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 317 318 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 319 Expect(Config.SpaceFields().GUID).To(Equal("my-space-guid")) 320 Expect(Config.AccessToken()).To(Equal("my_access_token")) 321 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 322 323 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 324 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("http://api.example.com")) 325 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 326 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 327 "username": "user@example.com", 328 "password": "password", 329 })) 330 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 331 }) 332 }) 333 334 Describe("where there are no available orgs", func() { 335 BeforeEach(func() { 336 orgRepo.ListOrgsReturns([]models.Organization{}, nil) 337 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{}) 338 }) 339 340 It("does not as the user to select an org", func() { 341 ui.Inputs = []string{"http://api.example.com", "user@example.com", "password"} 342 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 343 344 Expect(Config.OrganizationFields().GUID).To(Equal("")) 345 Expect(Config.SpaceFields().GUID).To(Equal("")) 346 Expect(Config.AccessToken()).To(Equal("my_access_token")) 347 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 348 349 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 350 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("http://api.example.com")) 351 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 352 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 353 "username": "user@example.com", 354 "password": "password", 355 })) 356 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 357 }) 358 }) 359 360 Describe("when there is only a single org and no spaces", func() { 361 BeforeEach(func() { 362 orgRepo.ListOrgsReturns([]models.Organization{org}, nil) 363 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{}) 364 }) 365 366 It("does not ask the user to select a space", func() { 367 ui.Inputs = []string{"http://api.example.com", "user@example.com", "password"} 368 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 369 370 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 371 Expect(Config.SpaceFields().GUID).To(Equal("")) 372 Expect(Config.AccessToken()).To(Equal("my_access_token")) 373 Expect(Config.RefreshToken()).To(Equal("my_refresh_token")) 374 375 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 376 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("http://api.example.com")) 377 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 378 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 379 "username": "user@example.com", 380 "password": "password", 381 })) 382 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 383 }) 384 }) 385 386 Describe("login prompts", func() { 387 BeforeEach(func() { 388 authRepo.GetLoginPromptsAndSaveUAAServerURLReturns(map[string]coreconfig.AuthPrompt{ 389 "account_number": { 390 DisplayName: "Account Number", 391 Type: coreconfig.AuthPromptTypeText, 392 }, 393 "username": { 394 DisplayName: "Username", 395 Type: coreconfig.AuthPromptTypeText, 396 }, 397 "passcode": { 398 DisplayName: "It's a passcode, what you want it to be???", 399 Type: coreconfig.AuthPromptTypePassword, 400 }, 401 "password": { 402 DisplayName: "Your Password", 403 Type: coreconfig.AuthPromptTypePassword, 404 }, 405 }, nil) 406 }) 407 408 Context("when the user does not provide the --sso flag", func() { 409 It("prompts the user for 'password' prompt and any text type prompt", func() { 410 ui.Inputs = []string{"api.example.com", "the-username", "the-account-number", "the-password"} 411 412 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 413 414 Expect(ui.Prompts).To(ContainSubstrings( 415 []string{"API endpoint"}, 416 []string{"Account Number"}, 417 []string{"Username"}, 418 )) 419 Expect(ui.PasswordPrompts).To(ContainSubstrings([]string{"Your Password"})) 420 Expect(ui.PasswordPrompts).ToNot(ContainSubstrings( 421 []string{"passcode"}, 422 )) 423 424 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 425 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 426 "account_number": "the-account-number", 427 "username": "the-username", 428 "password": "the-password", 429 })) 430 }) 431 }) 432 433 Context("when the user does provide the --sso flag", func() { 434 It("only prompts the user for the passcode type prompts", func() { 435 Flags = []string{"--sso", "-a", "api.example.com"} 436 ui.Inputs = []string{"the-one-time-code"} 437 438 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 439 440 Expect(ui.Prompts).To(BeEmpty()) 441 Expect(ui.PasswordPrompts).To(ContainSubstrings([]string{"passcode"})) 442 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 443 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 444 "passcode": "the-one-time-code", 445 })) 446 }) 447 }) 448 449 Context("when the user provides the --sso-passcode flag", func() { 450 It("does not prompt the user for the passcode type prompts", func() { 451 Flags = []string{"--sso-passcode", "the-one-time-code", "-a", "api.example.com"} 452 453 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 454 455 Expect(ui.Prompts).To(BeEmpty()) 456 Expect(ui.PasswordPrompts).To(BeEmpty()) 457 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 458 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 459 "passcode": "the-one-time-code", 460 })) 461 }) 462 }) 463 464 Context("when the user does provides both the --sso and --sso-passcode flags", func() { 465 It("errors with usage error and does not try to authenticate", func() { 466 Flags = []string{"--sso", "-sso-passcode", "the-one-time-code", "-a", "api.example.com"} 467 ui.Inputs = []string{"the-one-time-code"} 468 469 execution := testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 470 Expect(execution).To(BeFalse()) 471 472 Expect(authRepo.AuthenticateCallCount()).To(Equal(0)) 473 }) 474 }) 475 476 Context("when multiple prompts of password type are given", func() { 477 BeforeEach(func() { 478 authRepo.GetLoginPromptsAndSaveUAAServerURLReturns(map[string]coreconfig.AuthPrompt{ 479 "username": { 480 DisplayName: "Username", 481 Type: coreconfig.AuthPromptTypeText, 482 }, 483 "otherpass1": { 484 DisplayName: "some secure thing like mfa", 485 Type: coreconfig.AuthPromptTypePassword, 486 }, 487 "password": { 488 DisplayName: "Your Password", 489 Type: coreconfig.AuthPromptTypePassword, 490 }, 491 }, nil) 492 }) 493 494 It("prompts for the password first", func() { 495 ui.Inputs = []string{"api.example.com", "the-username", "the-password", "other-secret"} 496 497 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 498 499 Expect(ui.PasswordPrompts).To(Equal([]string{ 500 "Your Password", 501 "some secure thing like mfa", 502 })) 503 }) 504 }) 505 506 It("takes the password from the -p flag", func() { 507 Flags = []string{"-p", "the-password"} 508 ui.Inputs = []string{"api.example.com", "the-username", "the-account-number", "the-pin"} 509 510 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 511 512 Expect(ui.PasswordPrompts).ToNot(ContainSubstrings([]string{"Your Password"})) 513 Expect(authRepo.AuthenticateCallCount()).To(Equal(1)) 514 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 515 "account_number": "the-account-number", 516 "username": "the-username", 517 "password": "the-password", 518 })) 519 }) 520 521 It("tries 3 times for the password-type prompts", func() { 522 authRepo.AuthenticateReturns(errors.New("Error authenticating.")) 523 ui.Inputs = []string{"api.example.com", "the-username", "the-account-number", 524 "the-password-1", "the-password-2", "the-password-3"} 525 526 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 527 528 Expect(authRepo.AuthenticateCallCount()).To(Equal(3)) 529 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 530 "username": "the-username", 531 "account_number": "the-account-number", 532 "password": "the-password-1", 533 })) 534 Expect(authRepo.AuthenticateArgsForCall(1)).To(Equal(map[string]string{ 535 "username": "the-username", 536 "account_number": "the-account-number", 537 "password": "the-password-2", 538 })) 539 Expect(authRepo.AuthenticateArgsForCall(2)).To(Equal(map[string]string{ 540 "username": "the-username", 541 "account_number": "the-account-number", 542 "password": "the-password-3", 543 })) 544 545 Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"})) 546 }) 547 548 It("prompts user for password again if password given on the cmd line fails", func() { 549 authRepo.AuthenticateReturns(errors.New("Error authenticating.")) 550 551 Flags = []string{"-p", "the-password-1"} 552 553 ui.Inputs = []string{"api.example.com", "the-username", "the-account-number", 554 "the-password-2", "the-password-3"} 555 556 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 557 558 Expect(authRepo.AuthenticateCallCount()).To(Equal(3)) 559 Expect(authRepo.AuthenticateArgsForCall(0)).To(Equal(map[string]string{ 560 "username": "the-username", 561 "account_number": "the-account-number", 562 "password": "the-password-1", 563 })) 564 Expect(authRepo.AuthenticateArgsForCall(1)).To(Equal(map[string]string{ 565 "username": "the-username", 566 "account_number": "the-account-number", 567 "password": "the-password-2", 568 })) 569 Expect(authRepo.AuthenticateArgsForCall(2)).To(Equal(map[string]string{ 570 "username": "the-username", 571 "account_number": "the-account-number", 572 "password": "the-password-3", 573 })) 574 575 Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"})) 576 }) 577 }) 578 }) 579 580 Describe("updates to the config", func() { 581 BeforeEach(func() { 582 Config.SetAPIEndpoint("api.the-old-endpoint.com") 583 Config.SetAccessToken("the-old-access-token") 584 Config.SetRefreshToken("the-old-refresh-token") 585 endpointRepo.GetCCInfoStub = func(endpoint string) (*coreconfig.CCInfo, string, error) { 586 return &coreconfig.CCInfo{ 587 APIVersion: apiVersion, 588 AuthorizationEndpoint: "auth/endpoint", 589 DopplerEndpoint: "doppler/endpoint", 590 MinCLIVersion: minCLIVersion, 591 MinRecommendedCLIVersion: minRecommendedCLIVersion, 592 SSHOAuthClient: "some-client", 593 RoutingAPIEndpoint: "routing/endpoint", 594 }, endpoint, nil 595 } 596 597 }) 598 599 JustBeforeEach(func() { 600 testcmd.RunCLICommand("login", Flags, nil, updateCommandDependency, false, ui) 601 }) 602 603 var ItShowsTheTarget = func() { 604 It("shows the target", func() { 605 Expect(ui.ShowConfigurationCalled).To(BeTrue()) 606 }) 607 } 608 609 var ItDoesntShowTheTarget = func() { 610 It("does not show the target info", func() { 611 Expect(ui.ShowConfigurationCalled).To(BeFalse()) 612 }) 613 } 614 615 var ItFails = func() { 616 It("fails", func() { 617 Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"})) 618 }) 619 } 620 621 var ItSucceeds = func() { 622 It("runs successfully", func() { 623 Expect(ui.Outputs()).ToNot(ContainSubstrings([]string{"FAILED"})) 624 Expect(ui.Outputs()).To(ContainSubstrings([]string{"OK"})) 625 }) 626 } 627 628 Describe("when the user is setting an API", func() { 629 BeforeEach(func() { 630 Flags = []string{"-a", "https://api.the-server.com", "-u", "the-user-name", "-p", "the-password"} 631 }) 632 633 Describe("when the --skip-ssl-validation flag is provided", func() { 634 BeforeEach(func() { 635 Flags = append(Flags, "--skip-ssl-validation") 636 }) 637 638 Describe("setting api endpoint is successful", func() { 639 BeforeEach(func() { 640 Config.SetSSLDisabled(false) 641 }) 642 643 ItSucceeds() 644 ItShowsTheTarget() 645 646 It("stores the API endpoint and the skip-ssl flag", func() { 647 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 648 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("https://api.the-server.com")) 649 Expect(Config.IsSSLDisabled()).To(BeTrue()) 650 }) 651 }) 652 653 Describe("setting api endpoint failed", func() { 654 BeforeEach(func() { 655 Config.SetSSLDisabled(true) 656 endpointRepo.GetCCInfoReturns(nil, "", errors.New("API endpoint not found")) 657 }) 658 659 ItFails() 660 ItDoesntShowTheTarget() 661 662 It("clears the entire config", func() { 663 Expect(Config.APIEndpoint()).To(BeEmpty()) 664 Expect(Config.IsSSLDisabled()).To(BeFalse()) 665 Expect(Config.AccessToken()).To(BeEmpty()) 666 Expect(Config.RefreshToken()).To(BeEmpty()) 667 Expect(Config.OrganizationFields().GUID).To(BeEmpty()) 668 Expect(Config.SpaceFields().GUID).To(BeEmpty()) 669 }) 670 }) 671 }) 672 673 Describe("when the --skip-ssl-validation flag is not provided", func() { 674 Describe("setting api endpoint is successful", func() { 675 BeforeEach(func() { 676 Config.SetSSLDisabled(true) 677 }) 678 679 ItSucceeds() 680 ItShowsTheTarget() 681 682 It("updates the API endpoint and enables SSL validation", func() { 683 Expect(endpointRepo.GetCCInfoCallCount()).To(Equal(1)) 684 Expect(endpointRepo.GetCCInfoArgsForCall(0)).To(Equal("https://api.the-server.com")) 685 Expect(Config.IsSSLDisabled()).To(BeFalse()) 686 }) 687 }) 688 689 Describe("setting api endpoint failed", func() { 690 BeforeEach(func() { 691 Config.SetSSLDisabled(true) 692 endpointRepo.GetCCInfoReturns(nil, "", errors.New("API endpoint not found")) 693 }) 694 695 ItFails() 696 ItDoesntShowTheTarget() 697 698 It("clears the entire config", func() { 699 Expect(Config.APIEndpoint()).To(BeEmpty()) 700 Expect(Config.IsSSLDisabled()).To(BeFalse()) 701 Expect(Config.AccessToken()).To(BeEmpty()) 702 Expect(Config.RefreshToken()).To(BeEmpty()) 703 Expect(Config.OrganizationFields().GUID).To(BeEmpty()) 704 Expect(Config.SpaceFields().GUID).To(BeEmpty()) 705 }) 706 }) 707 }) 708 709 Describe("when there is an invalid SSL cert", func() { 710 BeforeEach(func() { 711 endpointRepo.GetCCInfoReturns(nil, "", errors.NewInvalidSSLCert("https://bobs-burgers.com", "SELF SIGNED SADNESS")) 712 ui.Inputs = []string{"bobs-burgers.com"} 713 }) 714 715 It("fails and suggests the user skip SSL validation", func() { 716 Expect(ui.Outputs()).To(ContainSubstrings( 717 []string{"FAILED"}, 718 []string{"SSL Cert", "https://bobs-burgers.com"}, 719 []string{"TIP", "login", "--skip-ssl-validation"}, 720 )) 721 }) 722 723 ItDoesntShowTheTarget() 724 }) 725 726 Describe("when the api version is older than the minimum version", func() { 727 BeforeEach(func() { 728 apiVersion = "2.68.0" 729 }) 730 731 It("prints a warning", func() { 732 Expect(ui.WarnOutputs[0]).To(ContainSubstring("Your API version is no longer supported. Upgrade to a newer version of the API.")) 733 }) 734 }) 735 }) 736 737 Describe("when user is logging in and not setting the api endpoint", func() { 738 BeforeEach(func() { 739 Flags = []string{"-u", "the-user-name", "-p", "the-password"} 740 }) 741 742 Describe("when the --skip-ssl-validation flag is provided", func() { 743 BeforeEach(func() { 744 Flags = append(Flags, "--skip-ssl-validation") 745 Config.SetSSLDisabled(false) 746 }) 747 748 It("disables SSL validation", func() { 749 Expect(Config.IsSSLDisabled()).To(BeTrue()) 750 }) 751 }) 752 753 Describe("when the --skip-ssl-validation flag is not provided", func() { 754 BeforeEach(func() { 755 Config.SetSSLDisabled(true) 756 }) 757 758 It("should not change config's SSLDisabled flag", func() { 759 Expect(Config.IsSSLDisabled()).To(BeTrue()) 760 }) 761 }) 762 763 Describe("and the login fails authenticaton", func() { 764 BeforeEach(func() { 765 authRepo.AuthenticateReturns(errors.New("Error authenticating.")) 766 767 Config.SetSSLDisabled(true) 768 769 Flags = []string{"-u", "user@example.com"} 770 ui.Inputs = []string{"password", "password2", "password3", "password4"} 771 }) 772 773 ItFails() 774 ItShowsTheTarget() 775 776 It("does not change the api endpoint or SSL setting in the config", func() { 777 Expect(Config.APIEndpoint()).To(Equal("api.the-old-endpoint.com")) 778 Expect(Config.IsSSLDisabled()).To(BeTrue()) 779 }) 780 781 It("clears Access Token, Refresh Token, Org, and Space in the config", func() { 782 Expect(Config.AccessToken()).To(BeEmpty()) 783 Expect(Config.RefreshToken()).To(BeEmpty()) 784 Expect(Config.OrganizationFields().GUID).To(BeEmpty()) 785 Expect(Config.SpaceFields().GUID).To(BeEmpty()) 786 }) 787 }) 788 }) 789 790 Describe("and the login fails to target an org", func() { 791 BeforeEach(func() { 792 Flags = []string{"-u", "user@example.com", "-p", "password", "-o", "nonexistentorg", "-s", "my-space"} 793 orgRepo.FindByNameReturns(models.Organization{}, errors.New("No org")) 794 Config.SetSSLDisabled(true) 795 }) 796 797 ItFails() 798 ItShowsTheTarget() 799 800 It("does not update the api endpoint or ssl setting in the config", func() { 801 Expect(Config.APIEndpoint()).To(Equal("api.the-old-endpoint.com")) 802 Expect(Config.IsSSLDisabled()).To(BeTrue()) 803 }) 804 805 It("clears Org, and Space in the config", func() { 806 Expect(Config.OrganizationFields().GUID).To(BeEmpty()) 807 Expect(Config.SpaceFields().GUID).To(BeEmpty()) 808 }) 809 }) 810 811 Describe("and the login fails to target a space", func() { 812 BeforeEach(func() { 813 Flags = []string{"-u", "user@example.com", "-p", "password", "-o", "my-new-org", "-s", "nonexistent"} 814 orgRepo.FindByNameReturns(org, nil) 815 spaceRepo.FindByNameReturns(models.Space{}, errors.New("find-by-name-err")) 816 817 Config.SetSSLDisabled(true) 818 }) 819 820 ItFails() 821 ItShowsTheTarget() 822 823 It("does not update the api endpoint or ssl setting in the config", func() { 824 Expect(Config.APIEndpoint()).To(Equal("api.the-old-endpoint.com")) 825 Expect(Config.IsSSLDisabled()).To(BeTrue()) 826 }) 827 828 It("updates the org in the config", func() { 829 Expect(Config.OrganizationFields().GUID).To(Equal("my-new-org-guid")) 830 }) 831 832 It("clears the space in the config", func() { 833 Expect(Config.SpaceFields().GUID).To(BeEmpty()) 834 }) 835 }) 836 837 Describe("and the login succeeds", func() { 838 BeforeEach(func() { 839 orgRepo.FindByNameReturns(models.Organization{ 840 OrganizationFields: models.OrganizationFields{ 841 Name: "new-org", 842 GUID: "new-org-guid", 843 }, 844 }, nil) 845 846 space1 := models.Space{} 847 space1.GUID = "new-space-guid" 848 space1.Name = "new-space-name" 849 spaceRepo.ListSpacesStub = listSpacesStub([]models.Space{space1}) 850 spaceRepo.FindByNameReturns(space1, nil) 851 852 authRepo.AuthenticateStub = func(credentials map[string]string) error { 853 Config.SetAccessToken("new_access_token") 854 Config.SetRefreshToken("new_refresh_token") 855 return nil 856 } 857 858 Flags = []string{"-u", "user@example.com", "-p", "password", "-o", "new-org", "-s", "new-space"} 859 860 Config.SetAPIEndpoint("api.the-old-endpoint.com") 861 Config.SetSSLDisabled(true) 862 }) 863 864 ItSucceeds() 865 ItShowsTheTarget() 866 867 It("does not update the api endpoint or SSL setting", func() { 868 Expect(Config.APIEndpoint()).To(Equal("api.the-old-endpoint.com")) 869 Expect(Config.IsSSLDisabled()).To(BeTrue()) 870 }) 871 872 It("updates the config", func() { 873 Expect(Config.AccessToken()).To(Equal("new_access_token")) 874 Expect(Config.RefreshToken()).To(Equal("new_refresh_token")) 875 Expect(Config.OrganizationFields().GUID).To(Equal("new-org-guid")) 876 Expect(Config.SpaceFields().GUID).To(Equal("new-space-guid")) 877 878 Expect(Config.APIVersion()).To(Equal("100.200.300")) 879 Expect(Config.AuthenticationEndpoint()).To(Equal("auth/endpoint")) 880 Expect(Config.SSHOAuthClient()).To(Equal("some-client")) 881 Expect(Config.MinCLIVersion()).To(Equal("1.0.0")) 882 Expect(Config.MinRecommendedCLIVersion()).To(Equal("1.0.0")) 883 Expect(Config.DopplerEndpoint()).To(Equal("doppler/endpoint")) 884 Expect(Config.RoutingAPIEndpoint()).To(Equal("routing/endpoint")) 885 886 }) 887 }) 888 889 Describe("when a previous user authenticated with a client grant type", func() { 890 BeforeEach(func() { 891 Config.SetUAAGrantType("client_credentials") 892 }) 893 894 Context("when the current user logs in with a password grant type", func() { 895 BeforeEach(func() { 896 Flags = []string{"-u", "the-user-name", "-p", "the-password"} 897 }) 898 899 It("displays an error informing the user to log out and returns an error", func() { 900 Expect(ui.Outputs()).To(ContainSubstrings([]string{"FAILED"})) 901 Expect(ui.Outputs()).To(ContainSubstrings([]string{"Service account currently logged in. Use 'cf logout' to log out service account and try again."})) 902 }) 903 }) 904 }) 905 }) 906 })