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 }