github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+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 refreshedToken string
   172  		var apiErr error
   173  
   174  		JustBeforeEach(func() {
   175  			refreshedToken, apiErr = auth.RefreshAuthToken()
   176  		})
   177  
   178  		Context("when the refresh token has expired", func() {
   179  			BeforeEach(func() {
   180  				setupTestServer(refreshTokenExpiredRequestError)
   181  			})
   182  			It("the returns the reauthentication error message", func() {
   183  				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."))
   184  			})
   185  		})
   186  		Context("when there is a UAA error", func() {
   187  			BeforeEach(func() {
   188  				setupTestServer(errorLoginRequest)
   189  			})
   190  
   191  			It("returns the API error", func() {
   192  				Expect(apiErr).NotTo(BeNil())
   193  			})
   194  		})
   195  	})
   196  })
   197  
   198  var authHeaders = http.Header{
   199  	"accept":        {"application/json"},
   200  	"content-type":  {"application/x-www-form-urlencoded"},
   201  	"authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("cf:"))},
   202  }
   203  
   204  var successfulLoginRequest = testnet.TestRequest{
   205  	Method:  "POST",
   206  	Path:    "/oauth/token",
   207  	Header:  authHeaders,
   208  	Matcher: successfulLoginMatcher,
   209  	Response: testnet.TestResponse{
   210  		Status: http.StatusOK,
   211  		Body: `
   212  {
   213    "access_token": "my_access_token",
   214    "token_type": "BEARER",
   215    "refresh_token": "my_refresh_token",
   216    "scope": "openid",
   217    "expires_in": 98765
   218  } `},
   219  }
   220  
   221  var successfulLoginMatcher = func(request *http.Request) {
   222  	err := request.ParseForm()
   223  	if err != nil {
   224  		Fail(fmt.Sprintf("Failed to parse form: %s", err))
   225  		return
   226  	}
   227  
   228  	Expect(request.Form.Get("username")).To(Equal("foo@example.com"))
   229  	Expect(request.Form.Get("password")).To(Equal("bar"))
   230  	Expect(request.Form.Get("grant_type")).To(Equal("password"))
   231  	Expect(request.Form.Get("scope")).To(Equal(""))
   232  }
   233  
   234  var unsuccessfulLoginRequest = testnet.TestRequest{
   235  	Method: "POST",
   236  	Path:   "/oauth/token",
   237  	Response: testnet.TestResponse{
   238  		Status: http.StatusUnauthorized,
   239  	},
   240  }
   241  var refreshTokenExpiredRequestError = testnet.TestRequest{
   242  	Method: "POST",
   243  	Path:   "/oauth/token",
   244  	Response: testnet.TestResponse{
   245  		Status: http.StatusUnauthorized,
   246  		Body: `
   247  {
   248  	"error": "invalid_token",
   249  	"error_description": "Invalid auth token: Invalid refresh token (expired): eyJhbGckjsdfdf"
   250  }
   251  `},
   252  }
   253  
   254  var errorLoginRequest = testnet.TestRequest{
   255  	Method: "POST",
   256  	Path:   "/oauth/token",
   257  	Response: testnet.TestResponse{
   258  		Status: http.StatusInternalServerError,
   259  	},
   260  }
   261  
   262  var errorMaskedAsSuccessLoginRequest = testnet.TestRequest{
   263  	Method: "POST",
   264  	Path:   "/oauth/token",
   265  	Response: testnet.TestResponse{
   266  		Status: http.StatusOK,
   267  		Body: `
   268  {
   269  	"error": {
   270  		"error": "rest_client_error",
   271  		"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"
   272  	}
   273  }
   274  `},
   275  }
   276  
   277  var loginServerLoginRequest = testnet.TestRequest{
   278  	Method: "GET",
   279  	Path:   "/login",
   280  	Response: testnet.TestResponse{
   281  		Status: http.StatusOK,
   282  		Body: `
   283  {
   284  	"timestamp":"2013-12-18T11:26:53-0700",
   285  	"app":{
   286  		"artifact":"cloudfoundry-identity-uaa",
   287  		"description":"User Account and Authentication Service",
   288  		"name":"UAA",
   289  		"version":"1.4.7"
   290  	},
   291  	"commit_id":"2701cc8",
   292  	"links":{
   293  	    "register":"https://console.run.pivotal.io/register",
   294  	    "passwd":"https://console.run.pivotal.io/password_resets/new",
   295  	    "home":"https://console.run.pivotal.io",
   296  	    "support":"https://support.cloudfoundry.com/home",
   297  	    "login":"https://login.run.pivotal.io",
   298  	    "uaa":"https://uaa.run.pivotal.io"
   299  	 },
   300  	"prompts":{
   301  		"username": ["text","Email"],
   302  		"pin": ["password", "PIN Number"]
   303  	}
   304  }`,
   305  	},
   306  }
   307  
   308  var loginServerLoginFailureRequest = testnet.TestRequest{
   309  	Method: "GET",
   310  	Path:   "/login",
   311  	Response: testnet.TestResponse{
   312  		Status: http.StatusInternalServerError,
   313  	},
   314  }
   315  
   316  var uaaServerLoginRequest = testnet.TestRequest{
   317  	Method: "GET",
   318  	Path:   "/login",
   319  	Response: testnet.TestResponse{
   320  		Status: http.StatusOK,
   321  		Body: `
   322  {
   323  	"timestamp":"2013-12-18T11:26:53-0700",
   324  	"app":{
   325  		"artifact":"cloudfoundry-identity-uaa",
   326  		"description":"User Account and Authentication Service",
   327  		"name":"UAA",
   328  		"version":"1.4.7"
   329  	},
   330  	"commit_id":"2701cc8",
   331  	"prompts":{
   332  		"username": ["text","Email"],
   333  		"pin": ["password", "PIN Number"]
   334  	}
   335  }`,
   336  	},
   337  }