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

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