github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/integration/v7/isolated/auth_command_test.go (about)

     1  package isolated
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"path/filepath"
     7  
     8  	"code.cloudfoundry.org/cli/api/uaa/uaaversion"
     9  	"code.cloudfoundry.org/cli/integration/helpers"
    10  	"code.cloudfoundry.org/cli/util/configv3"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/gbytes"
    14  	. "github.com/onsi/gomega/gexec"
    15  )
    16  
    17  var _ = Describe("auth command", func() {
    18  	BeforeEach(func() {
    19  		helpers.SkipIfClientCredentialsTestMode()
    20  	})
    21  
    22  	Context("Help", func() {
    23  		It("displays the help information", func() {
    24  			session := helpers.CF("auth", "--help")
    25  			Eventually(session).Should(Say("NAME:"))
    26  			Eventually(session).Should(Say("auth - Authenticate non-interactively\n\n"))
    27  
    28  			Eventually(session).Should(Say("USAGE:"))
    29  			Eventually(session).Should(Say("cf auth USERNAME PASSWORD\n"))
    30  			Eventually(session).Should(Say("cf auth CLIENT_ID CLIENT_SECRET --client-credentials\n\n"))
    31  
    32  			Eventually(session).Should(Say("ENVIRONMENT VARIABLES:"))
    33  			Eventually(session).Should(Say(`CF_USERNAME=user\s+Authenticating user. Overridden if USERNAME argument is provided.`))
    34  			Eventually(session).Should(Say(`CF_PASSWORD=password\s+Password associated with user. Overridden if PASSWORD argument is provided.`))
    35  
    36  			Eventually(session).Should(Say("WARNING:"))
    37  			Eventually(session).Should(Say("Providing your password as a command line option is highly discouraged"))
    38  			Eventually(session).Should(Say("Your password may be visible to others and may be recorded in your shell history\n"))
    39  			Eventually(session).Should(Say("Consider using the CF_PASSWORD environment variable instead\n\n"))
    40  
    41  			Eventually(session).Should(Say("EXAMPLES:"))
    42  			Eventually(session).Should(Say("cf auth name@example\\.com \"my password\" \\(use quotes for passwords with a space\\)"))
    43  			Eventually(session).Should(Say("cf auth name@example\\.com \\\"\\\\\"password\\\\\"\\\" \\(escape quotes if used in password\\)\n\n"))
    44  
    45  			Eventually(session).Should(Say("OPTIONS:"))
    46  			Eventually(session).Should(Say("--client-credentials\\s+Use \\(non-user\\) service account \\(also called client credentials\\)\n"))
    47  			Eventually(session).Should(Say("--origin\\s+Indicates the identity provider to be used for authentication\n\n"))
    48  
    49  			Eventually(session).Should(Say("SEE ALSO:"))
    50  			Eventually(session).Should(Say("api, login, target"))
    51  
    52  			Eventually(session).Should(Exit(0))
    53  		})
    54  	})
    55  
    56  	When("no positional arguments are provided", func() {
    57  		Context("and no env variables are provided", func() {
    58  			It("errors-out with the help information", func() {
    59  				envWithoutLoginInfo := map[string]string{
    60  					"CF_USERNAME": "",
    61  					"CF_PASSWORD": "",
    62  				}
    63  				session := helpers.CFWithEnv(envWithoutLoginInfo, "auth")
    64  				Eventually(session.Err).Should(Say("Username and password not provided."))
    65  				Eventually(session).Should(Say("NAME:"))
    66  
    67  				Eventually(session).Should(Exit(1))
    68  			})
    69  		})
    70  
    71  		When("env variables are provided", func() {
    72  			It("authenticates the user", func() {
    73  				username, password := helpers.GetCredentials()
    74  				env := map[string]string{
    75  					"CF_USERNAME": username,
    76  					"CF_PASSWORD": password,
    77  				}
    78  				session := helpers.CFWithEnv(env, "auth")
    79  
    80  				Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
    81  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
    82  				Eventually(session).Should(Say("OK"))
    83  				Eventually(session).Should(Say("Use 'cf target' to view or set your target org and space"))
    84  
    85  				Eventually(session).Should(Exit(0))
    86  			})
    87  		})
    88  	})
    89  
    90  	When("only a username is provided", func() {
    91  		It("errors-out with a password required error and the help information", func() {
    92  			envWithoutLoginInfo := map[string]string{
    93  				"CF_USERNAME": "",
    94  				"CF_PASSWORD": "",
    95  			}
    96  			session := helpers.CFWithEnv(envWithoutLoginInfo, "auth", "some-user")
    97  			Eventually(session.Err).Should(Say("Password not provided."))
    98  			Eventually(session).Should(Say("NAME:"))
    99  
   100  			Eventually(session).Should(Exit(1))
   101  		})
   102  	})
   103  
   104  	When("only a password is provided", func() {
   105  		It("errors-out with a username required error and the help information", func() {
   106  			env := map[string]string{
   107  				"CF_USERNAME": "",
   108  				"CF_PASSWORD": "some-pass",
   109  			}
   110  			session := helpers.CFWithEnv(env, "auth")
   111  			Eventually(session.Err).Should(Say("Username not provided."))
   112  			Eventually(session).Should(Say("NAME:"))
   113  
   114  			Eventually(session).Should(Exit(1))
   115  		})
   116  	})
   117  
   118  	When("extra input is given", func() {
   119  		It("displays an 'unknown flag' error message", func() {
   120  			session := helpers.CF("auth", "some-username", "some-password", "-a", "api.bosh-lite.com")
   121  
   122  			Eventually(session.Err).Should(Say("Incorrect Usage: unknown flag `a'"))
   123  			Eventually(session).Should(Say("NAME:"))
   124  
   125  			Eventually(session).Should(Exit(1))
   126  		})
   127  	})
   128  
   129  	When("the API endpoint is not set", func() {
   130  		BeforeEach(func() {
   131  			helpers.UnsetAPI()
   132  		})
   133  
   134  		It("displays an error message", func() {
   135  			session := helpers.CF("auth", "some-username", "some-password")
   136  
   137  			Eventually(session).Should(Say("FAILED"))
   138  			Eventually(session.Err).Should(Say(`No API endpoint set\. Use 'cf login' or 'cf api' to target an endpoint\.`))
   139  
   140  			Eventually(session).Should(Exit(1))
   141  		})
   142  	})
   143  
   144  	When("no flags are set (logging in with password grant type)", func() {
   145  		When("the user provides an invalid username/password combo", func() {
   146  			BeforeEach(func() {
   147  				helpers.LoginCF()
   148  				helpers.TargetOrgAndSpace(ReadOnlyOrg, ReadOnlySpace)
   149  			})
   150  
   151  			It("clears the cached tokens and target info, then displays an error message", func() {
   152  				session := helpers.CF("auth", "some-username", "some-password")
   153  
   154  				Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   155  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
   156  				Eventually(session).Should(Say("FAILED"))
   157  				Eventually(session.Err).Should(Say(`Credentials were rejected, please try again\.`))
   158  				Eventually(session).Should(Exit(1))
   159  
   160  				// Verify that the user is not logged-in
   161  				targetSession1 := helpers.CF("target")
   162  				Eventually(targetSession1.Err).Should(Say(`Not logged in\. Use 'cf login' or 'cf login --sso' to log in\.`))
   163  				Eventually(targetSession1).Should(Say("FAILED"))
   164  				Eventually(targetSession1).Should(Exit(1))
   165  
   166  				// Verify that neither org nor space is targeted
   167  				helpers.LoginCF()
   168  				targetSession2 := helpers.CF("target")
   169  				Eventually(targetSession2).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   170  				Eventually(targetSession2).Should(Exit(0))
   171  			})
   172  		})
   173  
   174  		When("the username and password are valid", func() {
   175  			It("authenticates the user", func() {
   176  				username, password := helpers.GetCredentials()
   177  				session := helpers.CF("auth", username, password)
   178  
   179  				Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   180  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
   181  				Eventually(session).Should(Say("OK"))
   182  				Eventually(session).Should(Say("Use 'cf target' to view or set your target org and space"))
   183  
   184  				Eventually(session).Should(Exit(0))
   185  			})
   186  		})
   187  	})
   188  
   189  	When("the 'client-credentials' flag is set", func() {
   190  		When("the user provides an invalid client id/secret combo", func() {
   191  			BeforeEach(func() {
   192  				helpers.LoginCF()
   193  				helpers.TargetOrgAndSpace(ReadOnlyOrg, ReadOnlySpace)
   194  			})
   195  
   196  			It("clears the cached tokens and target info, then displays an error message", func() {
   197  				session := helpers.CF("auth", "some-client-id", "some-client-secret", "--client-credentials")
   198  
   199  				Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   200  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
   201  				Eventually(session).Should(Say("FAILED"))
   202  				Eventually(session.Err).Should(Say(`Credentials were rejected, please try again\.`))
   203  				Eventually(session).Should(Exit(1))
   204  
   205  				// Verify that the user is not logged-in
   206  				targetSession1 := helpers.CF("target")
   207  				Eventually(targetSession1.Err).Should(Say(`Not logged in\. Use 'cf login' or 'cf login --sso' to log in\.`))
   208  				Eventually(targetSession1).Should(Say("FAILED"))
   209  				Eventually(targetSession1).Should(Exit(1))
   210  
   211  				// Verify that neither org nor space is targeted
   212  				helpers.LoginCF()
   213  				targetSession2 := helpers.CF("target")
   214  				Eventually(targetSession2).Should(Say("No org or space targeted, use 'cf target -o ORG -s SPACE'"))
   215  				Eventually(targetSession2).Should(Exit(0))
   216  			})
   217  		})
   218  
   219  		When("the client id and client secret are valid", func() {
   220  			It("authenticates the user", func() {
   221  				clientID, clientSecret := helpers.SkipIfClientCredentialsNotSet()
   222  				session := helpers.CF("auth", clientID, clientSecret, "--client-credentials")
   223  
   224  				Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   225  				Eventually(session).Should(Say(`Authenticating\.\.\.`))
   226  				Eventually(session).Should(Say("OK"))
   227  				Eventually(session).Should(Say("Use 'cf target' to view or set your target org and space"))
   228  
   229  				Eventually(session).Should(Exit(0))
   230  			})
   231  
   232  			It("writes the client id but does not write the client secret to the config file", func() {
   233  				clientID, clientSecret := helpers.SkipIfClientCredentialsNotSet()
   234  				session := helpers.CF("auth", clientID, clientSecret, "--client-credentials")
   235  				Eventually(session).Should(Exit(0))
   236  
   237  				rawConfig, err := ioutil.ReadFile(filepath.Join(homeDir, ".cf", "config.json"))
   238  				Expect(err).NotTo(HaveOccurred())
   239  
   240  				Expect(string(rawConfig)).ToNot(ContainSubstring(clientSecret))
   241  
   242  				var configFile configv3.JSONConfig
   243  				err = json.Unmarshal(rawConfig, &configFile)
   244  
   245  				Expect(err).NotTo(HaveOccurred())
   246  				Expect(configFile.UAAOAuthClient).To(Equal(clientID))
   247  				Expect(configFile.UAAOAuthClientSecret).To(BeEmpty())
   248  				Expect(configFile.UAAGrantType).To(Equal("client_credentials"))
   249  			})
   250  		})
   251  	})
   252  
   253  	When("a user authenticates with valid client credentials", func() {
   254  		BeforeEach(func() {
   255  			clientID, clientSecret := helpers.SkipIfClientCredentialsNotSet()
   256  			session := helpers.CF("auth", clientID, clientSecret, "--client-credentials")
   257  			Eventually(session).Should(Exit(0))
   258  		})
   259  
   260  		When("a different user authenticates with valid password credentials", func() {
   261  			It("should fail authentication and display an error informing the user they need to log out", func() {
   262  				username, password := helpers.GetCredentials()
   263  				session := helpers.CF("auth", username, password)
   264  
   265  				Eventually(session).Should(Say("FAILED"))
   266  				Eventually(session.Err).Should(Say(`Service account currently logged in\. Use 'cf logout' to log out service account and try again\.`))
   267  				Eventually(session).Should(Exit(1))
   268  			})
   269  		})
   270  
   271  	})
   272  
   273  	When("the origin flag is set", func() {
   274  		When("the UAA version is too low to use the --origin flag", func() {
   275  			BeforeEach(func() {
   276  				helpers.SkipIfUAAVersionAtLeast(uaaversion.MinUAAClientVersion)
   277  			})
   278  			It("prints an error message", func() {
   279  				session := helpers.CF("auth", "some-username", "some-password", "--client-credentials", "--origin", "garbage")
   280  				Eventually(session.Err).Should(Say("Option '--origin' requires UAA API version 4.19.0 or higher. Update your Cloud Foundry instance."))
   281  				Eventually(session).Should(Say("FAILED"))
   282  				Eventually(session).Should(Exit(1))
   283  			})
   284  		})
   285  
   286  		When("the UAA version is recent enough to support the flag", func() {
   287  			BeforeEach(func() {
   288  				helpers.SkipIfUAAVersionLessThan(uaaversion.MinUAAClientVersion)
   289  			})
   290  			When("--client-credentials is also set", func() {
   291  				It("displays the appropriate error message", func() {
   292  					session := helpers.CF("auth", "some-username", "some-password", "--client-credentials", "--origin", "garbage")
   293  
   294  					Eventually(session.Err).Should(Say("Incorrect Usage: The following arguments cannot be used together: --client-credentials, --origin"))
   295  					Eventually(session).Should(Exit(1))
   296  				})
   297  			})
   298  
   299  			When("a user authenticates with valid user credentials for that origin", func() {
   300  				var (
   301  					username string
   302  					password string
   303  				)
   304  
   305  				BeforeEach(func() {
   306  					username, password = helpers.SkipIfOIDCCredentialsNotSet()
   307  				})
   308  
   309  				It("authenticates the user", func() {
   310  					session := helpers.CF("auth", username, password, "--origin", "cli-oidc-provider")
   311  
   312  					Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   313  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   314  					Eventually(session).Should(Say("OK"))
   315  					Eventually(session).Should(Say("Use 'cf target' to view or set your target org and space"))
   316  					Eventually(session).Should(Exit(0))
   317  				})
   318  			})
   319  
   320  			When("the user provides the default origin and valid credentials", func() {
   321  				It("authenticates the user", func() {
   322  					username, password := helpers.GetCredentials()
   323  					session := helpers.CF("auth", username, password, "--origin", "uaa")
   324  
   325  					Eventually(session).Should(Say("API endpoint: %s", helpers.GetAPI()))
   326  					Eventually(session).Should(Say(`Authenticating\.\.\.`))
   327  					Eventually(session).Should(Say("OK"))
   328  					Eventually(session).Should(Say("Use 'cf target' to view or set your target org and space"))
   329  					Eventually(session).Should(Exit(0))
   330  				})
   331  			})
   332  
   333  			When("when the user provides an invalid origin", func() {
   334  				It("returns an error", func() {
   335  					session := helpers.CF("auth", "some-user", "some-password", "--origin", "EA")
   336  					Eventually(session.Err).Should(Say("The origin provided is invalid."))
   337  					Eventually(session).Should(Say("FAILED"))
   338  					Eventually(session).Should(Exit(1))
   339  				})
   340  			})
   341  		})
   342  	})
   343  
   344  	Describe("Authenticating as a user, through a custom client", func() {
   345  		var (
   346  			session *Session
   347  		)
   348  
   349  		BeforeEach(func() {
   350  			customClientID, customClientSecret := helpers.SkipIfCustomClientCredentialsNotSet()
   351  
   352  			helpers.LoginCF()
   353  
   354  			helpers.SetConfig(func(config *configv3.Config) {
   355  				config.ConfigFile.UAAOAuthClient = customClientID
   356  				config.ConfigFile.UAAOAuthClientSecret = customClientSecret
   357  				config.ConfigFile.UAAGrantType = ""
   358  			})
   359  		})
   360  
   361  		It("fails and shows a helpful message", func() {
   362  			username, password := helpers.CreateUser()
   363  
   364  			session = helpers.CF("auth", username, password)
   365  
   366  			Eventually(session).Should(Exit(1))
   367  			Eventually(session.Err).Should(Say("Error: Support for manually writing your client credentials to config.json has been removed. For similar functionality please use `cf auth --client-credentials`."))
   368  		})
   369  	})
   370  })