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 }