github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/v7/auth_command_test.go (about)

     1  package v7_test
     2  
     3  import (
     4  	"github.com/LukasHeimann/cloudfoundrycli/v8/cf/configuration/coreconfig"
     5  	"errors"
     6  
     7  	"github.com/LukasHeimann/cloudfoundrycli/v8/api/uaa"
     8  	"github.com/LukasHeimann/cloudfoundrycli/v8/api/uaa/constant"
     9  	"github.com/LukasHeimann/cloudfoundrycli/v8/api/uaa/uaaversion"
    10  	"github.com/LukasHeimann/cloudfoundrycli/v8/command/commandfakes"
    11  	"github.com/LukasHeimann/cloudfoundrycli/v8/command/translatableerror"
    12  	. "github.com/LukasHeimann/cloudfoundrycli/v8/command/v7"
    13  	"github.com/LukasHeimann/cloudfoundrycli/v8/command/v7/v7fakes"
    14  	"github.com/LukasHeimann/cloudfoundrycli/v8/util/ui"
    15  	. "github.com/onsi/ginkgo"
    16  	. "github.com/onsi/gomega"
    17  	. "github.com/onsi/gomega/gbytes"
    18  )
    19  
    20  var _ = Describe("auth Command", func() {
    21  	var (
    22  		cmd             AuthCommand
    23  		testUI          *ui.UI
    24  		fakeActor       *v7fakes.FakeActor
    25  		fakeConfig      *commandfakes.FakeConfig
    26  		binaryName      string
    27  		err             error
    28  		k8sLoginPrompts map[string]coreconfig.AuthPrompt
    29  	)
    30  
    31  	BeforeEach(func() {
    32  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    33  		fakeActor = new(v7fakes.FakeActor)
    34  		fakeConfig = new(commandfakes.FakeConfig)
    35  
    36  		cmd = AuthCommand{
    37  			BaseCommand: BaseCommand{
    38  				UI:     testUI,
    39  				Config: fakeConfig,
    40  				Actor:  fakeActor,
    41  			},
    42  		}
    43  
    44  		binaryName = "faceman"
    45  		fakeConfig.BinaryNameReturns(binaryName)
    46  		fakeConfig.UAAOAuthClientReturns("cf")
    47  		fakeConfig.APIVersionReturns("3.99.0")
    48  		k8sLoginPrompts = map[string]coreconfig.AuthPrompt{
    49  			"k8s-auth-info": {
    50  				Entries: []string{"myuser"},
    51  			},
    52  		}
    53  	})
    54  
    55  	JustBeforeEach(func() {
    56  		err = cmd.Execute(nil)
    57  	})
    58  
    59  	When("--origin are set", func() {
    60  		BeforeEach(func() {
    61  			cmd.Origin = "some-origin"
    62  		})
    63  
    64  		When("the UAA is below the minimum API version", func() {
    65  			BeforeEach(func() {
    66  				fakeActor.GetUAAAPIVersionReturns(uaaversion.InvalidUAAClientVersion, nil)
    67  			})
    68  
    69  			It("returns an API version error", func() {
    70  				Expect(err).To(MatchError(translatableerror.MinimumUAAAPIVersionNotMetError{
    71  					Command:        "Option '--origin'",
    72  					MinimumVersion: uaaversion.MinUAAClientVersion,
    73  				}))
    74  			})
    75  		})
    76  
    77  		When("--client-credentials set", func() {
    78  			BeforeEach(func() {
    79  				cmd.ClientCredentials = true
    80  				fakeActor.GetUAAAPIVersionReturns(uaaversion.MinUAAClientVersion, nil)
    81  			})
    82  
    83  			It("returns an ArgumentCombinationError", func() {
    84  				Expect(err).To(MatchError(translatableerror.ArgumentCombinationError{
    85  					Args: []string{"--client-credentials", "--origin"},
    86  				}))
    87  			})
    88  		})
    89  	})
    90  
    91  	When("credentials are missing", func() {
    92  		When("username and password are both missing", func() {
    93  			It("raises an error", func() {
    94  				Expect(err).To(MatchError(translatableerror.MissingCredentialsError{
    95  					MissingUsername: true,
    96  					MissingPassword: true,
    97  				}))
    98  			})
    99  		})
   100  
   101  		When("username is missing", func() {
   102  			BeforeEach(func() {
   103  				cmd.RequiredArgs.Password = "mypassword"
   104  			})
   105  
   106  			It("raises an error", func() {
   107  				Expect(err).To(MatchError(translatableerror.MissingCredentialsError{
   108  					MissingUsername: true,
   109  				}))
   110  			})
   111  		})
   112  
   113  		When("password is missing", func() {
   114  			BeforeEach(func() {
   115  				cmd.RequiredArgs.Username = "myuser"
   116  			})
   117  
   118  			It("raises an error", func() {
   119  				Expect(err).To(MatchError(translatableerror.MissingCredentialsError{
   120  					MissingPassword: true,
   121  				}))
   122  			})
   123  
   124  			When("authenticating against Korifi", func() {
   125  				BeforeEach(func() {
   126  					fakeConfig.IsCFOnK8sReturns(true)
   127  					fakeActor.GetLoginPromptsReturns(k8sLoginPrompts, nil)
   128  				})
   129  
   130  				It("succeeds", func() {
   131  					Expect(err).NotTo(HaveOccurred())
   132  				})
   133  			})
   134  		})
   135  	})
   136  
   137  	When("there is an auth error", func() {
   138  		BeforeEach(func() {
   139  			cmd.RequiredArgs.Username = "foo"
   140  			cmd.RequiredArgs.Password = "bar"
   141  
   142  			fakeConfig.TargetReturns("some-api-target")
   143  			fakeActor.AuthenticateReturns(uaa.UnauthorizedError{Message: "some message"})
   144  		})
   145  
   146  		It("returns a BadCredentialsError", func() {
   147  			Expect(err).To(MatchError(uaa.UnauthorizedError{Message: "some message"}))
   148  		})
   149  	})
   150  
   151  	When("there is an account locked error", func() {
   152  		BeforeEach(func() {
   153  			cmd.RequiredArgs.Username = "foo"
   154  			cmd.RequiredArgs.Password = "bar"
   155  
   156  			fakeConfig.TargetReturns("some-api-target")
   157  			fakeActor.AuthenticateReturns(uaa.AccountLockedError{Message: "some message"})
   158  		})
   159  
   160  		It("returns a BadCredentialsError", func() {
   161  			Expect(err).To(MatchError(uaa.AccountLockedError{Message: "some message"}))
   162  		})
   163  	})
   164  
   165  	When("there is a non-auth error", func() {
   166  		var expectedError error
   167  
   168  		BeforeEach(func() {
   169  			cmd.RequiredArgs.Username = "foo"
   170  			cmd.RequiredArgs.Password = "bar"
   171  
   172  			fakeConfig.TargetReturns("some-api-target")
   173  			expectedError = errors.New("my humps")
   174  
   175  			fakeActor.AuthenticateReturns(expectedError)
   176  		})
   177  
   178  		It("returns the error", func() {
   179  			Expect(err).To(MatchError(expectedError))
   180  		})
   181  	})
   182  
   183  	When("there are no input errors", func() {
   184  		var (
   185  			testID     string
   186  			testSecret string
   187  		)
   188  
   189  		BeforeEach(func() {
   190  			testID = "hello"
   191  			testSecret = "goodbye"
   192  
   193  			fakeConfig.TargetReturns("some-api-target")
   194  		})
   195  
   196  		When("--client-credentials is set", func() {
   197  			BeforeEach(func() {
   198  				cmd.ClientCredentials = true
   199  				cmd.RequiredArgs.Username = testID
   200  				cmd.RequiredArgs.Password = testSecret
   201  			})
   202  
   203  			It("outputs API target information and clears the targeted org and space", func() {
   204  				Expect(err).ToNot(HaveOccurred())
   205  
   206  				Expect(testUI.Out).To(Say("API endpoint: %s", fakeConfig.Target()))
   207  				Expect(testUI.Out).To(Say(`Authenticating\.\.\.`))
   208  				Expect(testUI.Out).To(Say("OK"))
   209  				Expect(testUI.Out).To(Say("Use '%s target' to view or set your target org and space", binaryName))
   210  
   211  				Expect(fakeActor.AuthenticateCallCount()).To(Equal(1))
   212  				credentials, origin, grantType := fakeActor.AuthenticateArgsForCall(0)
   213  				ID := credentials["client_id"]
   214  				secret := credentials["client_secret"]
   215  				Expect(ID).To(Equal(testID))
   216  				Expect(secret).To(Equal(testSecret))
   217  				Expect(origin).To(BeEmpty())
   218  				Expect(grantType).To(Equal(constant.GrantTypeClientCredentials))
   219  			})
   220  		})
   221  
   222  		When("--client-credentials is not set", func() {
   223  			When("username and password are only provided as arguments", func() {
   224  				BeforeEach(func() {
   225  					cmd.RequiredArgs.Username = testID
   226  					cmd.RequiredArgs.Password = testSecret
   227  				})
   228  
   229  				When("the API version is older than the minimum supported API version for the v7 CLI", func() {
   230  					BeforeEach(func() {
   231  						fakeConfig.APIVersionReturns("3.83.0")
   232  					})
   233  					It("warns that the user is targeting an unsupported API version and that things may not work correctly", func() {
   234  						Expect(err).ToNot(HaveOccurred())
   235  						Expect(testUI.Out).To(Say("API endpoint: %s", fakeConfig.Target()))
   236  						Expect(testUI.Err).To(Say("Warning: Your targeted API's version \\(3.83.0\\) is less than the minimum supported API version \\(3.99.0\\). Some commands may not function correctly."))
   237  					})
   238  				})
   239  
   240  				When("the API version is empty", func() {
   241  					BeforeEach(func() {
   242  						fakeConfig.APIVersionReturns("")
   243  					})
   244  					It("prints a warning message", func() {
   245  						Expect(err).ToNot(HaveOccurred())
   246  						Expect(testUI.Err).To(Say("Warning: unable to determine whether targeted API's version meets minimum supported."))
   247  					})
   248  				})
   249  
   250  				It("should NOT warn that the user is targeting an unsupported API version", func() {
   251  					Expect(testUI.Err).ToNot(Say("is less than the minimum supported API version"))
   252  				})
   253  
   254  				It("outputs API target information and clears the targeted org and space", func() {
   255  					Expect(err).ToNot(HaveOccurred())
   256  
   257  					Expect(testUI.Out).To(Say("API endpoint: %s", fakeConfig.Target()))
   258  					Expect(testUI.Out).To(Say(`Authenticating\.\.\.`))
   259  					Expect(testUI.Out).To(Say("OK"))
   260  					Expect(testUI.Out).To(Say("Use '%s target' to view or set your target org and space", binaryName))
   261  
   262  					Expect(fakeActor.AuthenticateCallCount()).To(Equal(1))
   263  					credentials, origin, grantType := fakeActor.AuthenticateArgsForCall(0)
   264  					username := credentials["username"]
   265  					password := credentials["password"]
   266  					Expect(username).To(Equal(testID))
   267  					Expect(password).To(Equal(testSecret))
   268  					Expect(origin).To(BeEmpty())
   269  					Expect(grantType).To(Equal(constant.GrantTypePassword))
   270  				})
   271  			})
   272  
   273  			When("the username and password are provided in env variables", func() {
   274  				var (
   275  					envUsername string
   276  					envPassword string
   277  				)
   278  
   279  				BeforeEach(func() {
   280  					envUsername = "banana"
   281  					envPassword = "potato"
   282  					fakeConfig.CFUsernameReturns(envUsername)
   283  					fakeConfig.CFPasswordReturns(envPassword)
   284  				})
   285  
   286  				When("username and password are not also provided as arguments", func() {
   287  					It("authenticates with the values in the env variables", func() {
   288  						Expect(err).ToNot(HaveOccurred())
   289  
   290  						Expect(fakeActor.AuthenticateCallCount()).To(Equal(1))
   291  						credentials, origin, _ := fakeActor.AuthenticateArgsForCall(0)
   292  						username := credentials["username"]
   293  						password := credentials["password"]
   294  						Expect(username).To(Equal(envUsername))
   295  						Expect(password).To(Equal(envPassword))
   296  						Expect(origin).To(BeEmpty())
   297  					})
   298  				})
   299  
   300  				When("username and password are also provided as arguments", func() {
   301  					BeforeEach(func() {
   302  						cmd.RequiredArgs.Username = testID
   303  						cmd.RequiredArgs.Password = testSecret
   304  					})
   305  
   306  					It("authenticates with the values from the command line args", func() {
   307  						Expect(err).ToNot(HaveOccurred())
   308  
   309  						Expect(fakeActor.AuthenticateCallCount()).To(Equal(1))
   310  						credentials, origin, _ := fakeActor.AuthenticateArgsForCall(0)
   311  						username := credentials["username"]
   312  						password := credentials["password"]
   313  						Expect(username).To(Equal(testID))
   314  						Expect(password).To(Equal(testSecret))
   315  						Expect(origin).To(BeEmpty())
   316  					})
   317  				})
   318  			})
   319  
   320  			When("authenticating against Korifi", func() {
   321  				BeforeEach(func() {
   322  					cmd.RequiredArgs.Username = "myuser"
   323  					fakeConfig.IsCFOnK8sReturns(true)
   324  					fakeActor.GetLoginPromptsReturns(k8sLoginPrompts, nil)
   325  				})
   326  
   327  				When("the specified username doesn't match any k8s context", func() {
   328  					BeforeEach(func() {
   329  						cmd.RequiredArgs.Username = "some-unknown-user"
   330  					})
   331  
   332  					It("errors", func() {
   333  						Expect(err).To(MatchError(errors.New("kubernetes user not found in configuration: some-unknown-user")))
   334  					})
   335  				})
   336  
   337  				When("the prompts don't contain k8s authentication information", func() {
   338  					BeforeEach(func() {
   339  						k8sLoginPrompts = map[string]coreconfig.AuthPrompt{
   340  							"some-other-auth-info": {
   341  								Entries: []string{"myuser"},
   342  							},
   343  						}
   344  						fakeActor.GetLoginPromptsReturns(k8sLoginPrompts, nil)
   345  					})
   346  
   347  					It("errors", func() {
   348  						Expect(err).To(MatchError(errors.New("kubernetes login context is missing")))
   349  					})
   350  
   351  				})
   352  
   353  			})
   354  		})
   355  
   356  		When("a user has manually added their client credentials to the config file", func() {
   357  			BeforeEach(func() {
   358  				fakeConfig.UAAOAuthClientReturns("AClientsId")
   359  			})
   360  
   361  			When("the --client-credentials flag is not set", func() {
   362  				BeforeEach(func() {
   363  					cmd.ClientCredentials = false
   364  					cmd.RequiredArgs.Username = "some-username"
   365  					cmd.RequiredArgs.Password = "some-password"
   366  				})
   367  
   368  				It("fails with an error indicating manual client credentials are no longer supported in the config file", func() {
   369  					Expect(err).To(MatchError(translatableerror.ManualClientCredentialsError{}))
   370  				})
   371  			})
   372  		})
   373  	})
   374  
   375  	When("already logged in via client credentials", func() {
   376  		BeforeEach(func() {
   377  			fakeConfig.UAAGrantTypeReturns("client_credentials")
   378  		})
   379  
   380  		When("authenticating as a user", func() {
   381  			BeforeEach(func() {
   382  				cmd.ClientCredentials = false
   383  				cmd.RequiredArgs.Username = "some-username"
   384  				cmd.RequiredArgs.Password = "some-password"
   385  			})
   386  
   387  			It("returns an already logged in error", func() {
   388  				Expect(err).To(MatchError("Service account currently logged in. Use 'cf logout' to log out service account and try again."))
   389  				Expect(fakeConfig.UAAGrantTypeCallCount()).To(Equal(1))
   390  			})
   391  
   392  			It("the returned error is translatable", func() {
   393  				Expect(err).To(MatchError(translatableerror.PasswordGrantTypeLogoutRequiredError{}))
   394  			})
   395  		})
   396  
   397  		When("authenticating as a client", func() {
   398  			BeforeEach(func() {
   399  				cmd.ClientCredentials = true
   400  				cmd.RequiredArgs.Username = "some-client-id"
   401  				cmd.RequiredArgs.Password = "some-client-secret"
   402  			})
   403  			It("does not error", func() {
   404  				Expect(err).ToNot(HaveOccurred())
   405  				Expect(fakeConfig.UAAGrantTypeCallCount()).To(Equal(0))
   406  			})
   407  		})
   408  	})
   409  })