github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+incompatible/cf/api/authentication/authentication_test.go (about)

     1  package authentication_test
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  
     9  	"github.com/cloudfoundry/cli/cf/configuration/core_config"
    10  	"github.com/cloudfoundry/cli/cf/net"
    11  	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
    12  	testnet "github.com/cloudfoundry/cli/testhelpers/net"
    13  	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
    14  
    15  	. "github.com/cloudfoundry/cli/cf/api/authentication"
    16  	. "github.com/cloudfoundry/cli/testhelpers/matchers"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  var _ = Describe("AuthenticationRepository", func() {
    22  	var (
    23  		gateway    net.Gateway
    24  		testServer *httptest.Server
    25  		handler    *testnet.TestHandler
    26  		config     core_config.ReadWriter
    27  		auth       AuthenticationRepository
    28  	)
    29  
    30  	BeforeEach(func() {
    31  		config = testconfig.NewRepository()
    32  		gateway = net.NewUAAGateway(config, &testterm.FakeUI{})
    33  		auth = NewUAAAuthenticationRepository(gateway, config)
    34  	})
    35  
    36  	AfterEach(func() {
    37  		testServer.Close()
    38  	})
    39  
    40  	var setupTestServer = func(request testnet.TestRequest) {
    41  		testServer, handler = testnet.NewServer([]testnet.TestRequest{request})
    42  		config.SetAuthenticationEndpoint(testServer.URL)
    43  	}
    44  
    45  	Describe("authenticating", func() {
    46  		var err error
    47  
    48  		JustBeforeEach(func() {
    49  			err = auth.Authenticate(map[string]string{
    50  				"username": "foo@example.com",
    51  				"password": "bar",
    52  			})
    53  		})
    54  
    55  		Describe("when login succeeds", func() {
    56  			BeforeEach(func() {
    57  				setupTestServer(successfulLoginRequest)
    58  			})
    59  
    60  			It("stores the access and refresh tokens in the config", func() {
    61  				Expect(handler).To(HaveAllRequestsCalled())
    62  				Expect(err).NotTo(HaveOccurred())
    63  				Expect(config.AuthenticationEndpoint()).To(Equal(testServer.URL))
    64  				Expect(config.AccessToken()).To(Equal("BEARER my_access_token"))
    65  				Expect(config.RefreshToken()).To(Equal("my_refresh_token"))
    66  			})
    67  		})
    68  
    69  		Describe("when login fails", func() {
    70  			BeforeEach(func() {
    71  				setupTestServer(unsuccessfulLoginRequest)
    72  			})
    73  
    74  			It("returns an error", func() {
    75  				Expect(handler).To(HaveAllRequestsCalled())
    76  				Expect(err).NotTo(BeNil())
    77  				Expect(err.Error()).To(Equal("Credentials were rejected, please try again."))
    78  				Expect(config.AccessToken()).To(BeEmpty())
    79  				Expect(config.RefreshToken()).To(BeEmpty())
    80  			})
    81  		})
    82  
    83  		Describe("when an error occurs during login", func() {
    84  			BeforeEach(func() {
    85  				setupTestServer(errorLoginRequest)
    86  			})
    87  
    88  			It("returns a failure response", func() {
    89  				Expect(handler).To(HaveAllRequestsCalled())
    90  				Expect(err).To(HaveOccurred())
    91  				Expect(err.Error()).To(Equal("Server error, status code: 500, error code: , message: "))
    92  				Expect(config.AccessToken()).To(BeEmpty())
    93  			})
    94  		})
    95  
    96  		Describe("when the UAA server has an error but still returns a 200", func() {
    97  			BeforeEach(func() {
    98  				setupTestServer(errorMaskedAsSuccessLoginRequest)
    99  			})
   100  
   101  			It("returns an error", func() {
   102  				Expect(handler).To(HaveAllRequestsCalled())
   103  				Expect(err).To(HaveOccurred())
   104  				Expect(err.Error()).To(ContainSubstring("I/O error: uaa.10.244.0.22.xip.io; nested exception is java.net.UnknownHostException: uaa.10.244.0.22.xip.io"))
   105  				Expect(config.AccessToken()).To(BeEmpty())
   106  			})
   107  		})
   108  	})
   109  
   110  	Describe("getting login info", func() {
   111  		var (
   112  			apiErr  error
   113  			prompts map[string]core_config.AuthPrompt
   114  		)
   115  
   116  		JustBeforeEach(func() {
   117  			prompts, apiErr = auth.GetLoginPromptsAndSaveUAAServerURL()
   118  		})
   119  
   120  		Describe("when the login info API succeeds", func() {
   121  			BeforeEach(func() {
   122  				setupTestServer(loginServerLoginRequest)
   123  			})
   124  
   125  			It("does not return an error", func() {
   126  				Expect(apiErr).NotTo(HaveOccurred())
   127  			})
   128  
   129  			It("gets the login prompts", func() {
   130  				Expect(prompts).To(Equal(map[string]core_config.AuthPrompt{
   131  					"username": core_config.AuthPrompt{
   132  						DisplayName: "Email",
   133  						Type:        core_config.AuthPromptTypeText,
   134  					},
   135  					"pin": core_config.AuthPrompt{
   136  						DisplayName: "PIN Number",
   137  						Type:        core_config.AuthPromptTypePassword,
   138  					},
   139  				}))
   140  			})
   141  
   142  			It("saves the UAA server to the config", func() {
   143  				Expect(config.UaaEndpoint()).To(Equal("https://uaa.run.pivotal.io"))
   144  			})
   145  		})
   146  
   147  		Describe("when the login info API fails", func() {
   148  			BeforeEach(func() {
   149  				setupTestServer(loginServerLoginFailureRequest)
   150  			})
   151  
   152  			It("returns a failure response when the login info API fails", func() {
   153  				Expect(handler).To(HaveAllRequestsCalled())
   154  				Expect(apiErr).To(HaveOccurred())
   155  				Expect(prompts).To(BeEmpty())
   156  			})
   157  		})
   158  
   159  		Context("when the response does not contain links", func() {
   160  			BeforeEach(func() {
   161  				setupTestServer(uaaServerLoginRequest)
   162  			})
   163  
   164  			It("presumes that the authorization server is the UAA", func() {
   165  				Expect(config.UaaEndpoint()).To(Equal(config.AuthenticationEndpoint()))
   166  			})
   167  		})
   168  	})
   169  
   170  	Describe("refreshing the auth token", func() {
   171  		var apiErr error
   172  
   173  		JustBeforeEach(func() {
   174  			_, apiErr = auth.RefreshAuthToken()
   175  		})
   176  
   177  		Context("when the refresh token has expired", func() {
   178  			BeforeEach(func() {
   179  				setupTestServer(refreshTokenExpiredRequestError)
   180  			})
   181  			It("the returns the reauthentication error message", func() {
   182  				Expect(apiErr.Error()).To(Equal("Authentication has expired.  Please log back in to re-authenticate.\n\nTIP: Use `cf login -a <endpoint> -u <user> -o <org> -s <space>` to log back in and re-authenticate."))
   183  			})
   184  		})
   185  		Context("when there is a UAA error", func() {
   186  			BeforeEach(func() {
   187  				setupTestServer(errorLoginRequest)
   188  			})
   189  
   190  			It("returns the API error", func() {
   191  				Expect(apiErr).NotTo(BeNil())
   192  			})
   193  		})
   194  	})
   195  })
   196  
   197  var authHeaders = http.Header{
   198  	"accept":        {"application/json"},
   199  	"content-type":  {"application/x-www-form-urlencoded"},
   200  	"authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("cf:"))},
   201  }
   202  
   203  var successfulLoginRequest = testnet.TestRequest{
   204  	Method:  "POST",
   205  	Path:    "/oauth/token",
   206  	Header:  authHeaders,
   207  	Matcher: successfulLoginMatcher,
   208  	Response: testnet.TestResponse{
   209  		Status: http.StatusOK,
   210  		Body: `
   211  {
   212    "access_token": "my_access_token",
   213    "token_type": "BEARER",
   214    "refresh_token": "my_refresh_token",
   215    "scope": "openid",
   216    "expires_in": 98765
   217  } `},
   218  }
   219  
   220  var successfulLoginMatcher = func(request *http.Request) {
   221  	err := request.ParseForm()
   222  	if err != nil {
   223  		Fail(fmt.Sprintf("Failed to parse form: %s", err))
   224  		return
   225  	}
   226  
   227  	Expect(request.Form.Get("username")).To(Equal("foo@example.com"))
   228  	Expect(request.Form.Get("password")).To(Equal("bar"))
   229  	Expect(request.Form.Get("grant_type")).To(Equal("password"))
   230  	Expect(request.Form.Get("scope")).To(Equal(""))
   231  }
   232  
   233  var unsuccessfulLoginRequest = testnet.TestRequest{
   234  	Method: "POST",
   235  	Path:   "/oauth/token",
   236  	Response: testnet.TestResponse{
   237  		Status: http.StatusUnauthorized,
   238  	},
   239  }
   240  var refreshTokenExpiredRequestError = testnet.TestRequest{
   241  	Method: "POST",
   242  	Path:   "/oauth/token",
   243  	Response: testnet.TestResponse{
   244  		Status: http.StatusUnauthorized,
   245  		Body: `
   246  {
   247  	"error": "invalid_token",
   248  	"error_description": "Invalid auth token: Invalid refresh token (expired): eyJhbGckjsdfdf"
   249  }
   250  `},
   251  }
   252  
   253  var errorLoginRequest = testnet.TestRequest{
   254  	Method: "POST",
   255  	Path:   "/oauth/token",
   256  	Response: testnet.TestResponse{
   257  		Status: http.StatusInternalServerError,
   258  	},
   259  }
   260  
   261  var errorMaskedAsSuccessLoginRequest = testnet.TestRequest{
   262  	Method: "POST",
   263  	Path:   "/oauth/token",
   264  	Response: testnet.TestResponse{
   265  		Status: http.StatusOK,
   266  		Body: `
   267  {
   268  	"error": {
   269  		"error": "rest_client_error",
   270  		"error_description": "I/O error: uaa.10.244.0.22.xip.io; nested exception is java.net.UnknownHostException: uaa.10.244.0.22.xip.io"
   271  	}
   272  }
   273  `},
   274  }
   275  
   276  var loginServerLoginRequest = testnet.TestRequest{
   277  	Method: "GET",
   278  	Path:   "/login",
   279  	Response: testnet.TestResponse{
   280  		Status: http.StatusOK,
   281  		Body: `
   282  {
   283  	"timestamp":"2013-12-18T11:26:53-0700",
   284  	"app":{
   285  		"artifact":"cloudfoundry-identity-uaa",
   286  		"description":"User Account and Authentication Service",
   287  		"name":"UAA",
   288  		"version":"1.4.7"
   289  	},
   290  	"commit_id":"2701cc8",
   291  	"links":{
   292  	    "register":"https://console.run.pivotal.io/register",
   293  	    "passwd":"https://console.run.pivotal.io/password_resets/new",
   294  	    "home":"https://console.run.pivotal.io",
   295  	    "support":"https://support.cloudfoundry.com/home",
   296  	    "login":"https://login.run.pivotal.io",
   297  	    "uaa":"https://uaa.run.pivotal.io"
   298  	 },
   299  	"prompts":{
   300  		"username": ["text","Email"],
   301  		"pin": ["password", "PIN Number"]
   302  	}
   303  }`,
   304  	},
   305  }
   306  
   307  var loginServerLoginFailureRequest = testnet.TestRequest{
   308  	Method: "GET",
   309  	Path:   "/login",
   310  	Response: testnet.TestResponse{
   311  		Status: http.StatusInternalServerError,
   312  	},
   313  }
   314  
   315  var uaaServerLoginRequest = testnet.TestRequest{
   316  	Method: "GET",
   317  	Path:   "/login",
   318  	Response: testnet.TestResponse{
   319  		Status: http.StatusOK,
   320  		Body: `
   321  {
   322  	"timestamp":"2013-12-18T11:26:53-0700",
   323  	"app":{
   324  		"artifact":"cloudfoundry-identity-uaa",
   325  		"description":"User Account and Authentication Service",
   326  		"name":"UAA",
   327  		"version":"1.4.7"
   328  	},
   329  	"commit_id":"2701cc8",
   330  	"prompts":{
   331  		"username": ["text","Email"],
   332  		"pin": ["password", "PIN Number"]
   333  	}
   334  }`,
   335  	},
   336  }