1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/strings/stringprintf.h"
8#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
9#include "chrome/browser/supervised_user/supervised_user_refresh_token_fetcher.h"
10#include "chrome/test/base/testing_profile.h"
11#include "content/public/test/test_browser_thread_bundle.h"
12#include "google_apis/gaia/gaia_constants.h"
13#include "google_apis/gaia/gaia_oauth_client.h"
14#include "google_apis/gaia/gaia_urls.h"
15#include "google_apis/gaia/google_service_auth_error.h"
16#include "google_apis/gaia/oauth2_token_service.h"
17#include "net/base/net_errors.h"
18#include "net/base/url_util.h"
19#include "net/http/http_request_headers.h"
20#include "net/http/http_status_code.h"
21#include "net/url_request/test_url_fetcher_factory.h"
22#include "net/url_request/url_fetcher_delegate.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25namespace {
26
27const char kAccountId[] = "account_id";
28const char kDeviceId[] = "device_id";
29const char kDeviceName[] = "Compy";
30const char kSupervisedUserId[] = "abcdef";
31
32const char kAccessToken[] = "accesstoken";
33const char kAuthorizationCode[] = "authorizationcode";
34const char kSupervisedUserToken[] = "supervisedusertoken";
35const char kOAuth2RefreshToken[] = "refreshtoken";
36
37const char kIssueTokenResponseFormat[] =
38    "{"
39    "  \"code\": \"%s\""
40    "}";
41
42const char kGetRefreshTokenResponseFormat[] =
43    "{"
44    "  \"access_token\": \"<ignored>\","
45    "  \"expires_in\": 12345,"
46    "  \"refresh_token\": \"%s\""
47    "}";
48
49// Utility methods --------------------------------------------------
50
51// Slightly hacky way to extract a value from a URL-encoded POST request body.
52bool GetValueForKey(const std::string& encoded_string,
53                    const std::string& key,
54                    std::string* value) {
55  GURL url("http://example.com/?" + encoded_string);
56  return net::GetValueForKeyInQuery(url, key, value);
57}
58
59void SendResponse(net::TestURLFetcher* url_fetcher,
60                  const std::string& response) {
61  url_fetcher->set_status(
62      net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 0));
63  url_fetcher->set_response_code(net::HTTP_OK);
64  url_fetcher->SetResponseString(response);
65  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
66}
67
68void SetNetworkError(net::TestURLFetcher* url_fetcher, int error) {
69  url_fetcher->set_status(
70      net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
71  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
72}
73
74void SetHttpError(net::TestURLFetcher* url_fetcher, int error) {
75  url_fetcher->set_status(net::URLRequestStatus());
76  url_fetcher->set_response_code(error);
77  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
78}
79
80void VerifyTokenRequest(
81    std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests) {
82  ASSERT_EQ(1u, requests.size());
83  EXPECT_EQ(1u, requests[0].scopes.size());
84  EXPECT_EQ(1u, requests[0].scopes.count(GaiaConstants::kOAuth1LoginScope));
85}
86
87}  // namespace
88
89class SupervisedUserRefreshTokenFetcherTest : public testing::Test {
90 public:
91  SupervisedUserRefreshTokenFetcherTest();
92  virtual ~SupervisedUserRefreshTokenFetcherTest() {}
93
94 protected:
95  void StartFetching();
96  void StartFetchingWithDeviceId(const std::string& device_id);
97
98  net::TestURLFetcher* GetIssueTokenRequest();
99  net::TestURLFetcher* GetRefreshTokenRequest();
100
101  void MakeOAuth2TokenServiceRequestSucceed();
102  void MakeOAuth2TokenServiceRequestFail(GoogleServiceAuthError::State error);
103  void MakeIssueTokenRequestSucceed();
104  void MakeRefreshTokenFetchSucceed();
105
106  void Reset();
107
108  const GoogleServiceAuthError& error() const { return error_; }
109  const std::string& token() const { return token_; }
110
111 private:
112  void OnTokenFetched(const GoogleServiceAuthError& error,
113                      const std::string& token);
114
115  content::TestBrowserThreadBundle thread_bundle_;
116  TestingProfile profile_;
117  FakeProfileOAuth2TokenService oauth2_token_service_;
118  net::TestURLFetcherFactory url_fetcher_factory_;
119  scoped_ptr<SupervisedUserRefreshTokenFetcher> token_fetcher_;
120  std::string device_id_;
121
122  GoogleServiceAuthError error_;
123  std::string token_;
124  base::WeakPtrFactory<SupervisedUserRefreshTokenFetcherTest> weak_ptr_factory_;
125};
126
127SupervisedUserRefreshTokenFetcherTest::SupervisedUserRefreshTokenFetcherTest()
128    : error_(GoogleServiceAuthError::NONE),
129      weak_ptr_factory_(this) {}
130
131void SupervisedUserRefreshTokenFetcherTest::StartFetching() {
132  StartFetchingWithDeviceId(std::string());
133}
134
135void SupervisedUserRefreshTokenFetcherTest::StartFetchingWithDeviceId(
136    const std::string& device_id) {
137  device_id_ = device_id;
138  oauth2_token_service_.IssueRefreshToken(kOAuth2RefreshToken);
139  token_fetcher_ = SupervisedUserRefreshTokenFetcher::Create(
140      &oauth2_token_service_,
141      kAccountId,
142      device_id_,
143      profile_.GetRequestContext()).Pass();
144  token_fetcher_->Start(
145      kSupervisedUserId,
146      kDeviceName,
147      base::Bind(
148          &SupervisedUserRefreshTokenFetcherTest::OnTokenFetched,
149          weak_ptr_factory_.GetWeakPtr()));
150}
151
152net::TestURLFetcher*
153SupervisedUserRefreshTokenFetcherTest::GetIssueTokenRequest() {
154  net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
155  if (!url_fetcher)
156    return NULL;
157
158  EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_issue_token_url(),
159            url_fetcher->GetOriginalURL());
160  std::string access_token;
161  net::HttpRequestHeaders headers;
162  url_fetcher->GetExtraRequestHeaders(&headers);
163  EXPECT_TRUE(headers.GetHeader("Authorization", &access_token));
164  EXPECT_EQ(std::string("Bearer ") + kAccessToken, access_token);
165  const std::string upload_data = url_fetcher->upload_data();
166  std::string supervised_user_id;
167  EXPECT_TRUE(GetValueForKey(upload_data, "profile_id", &supervised_user_id));
168  EXPECT_EQ(kSupervisedUserId, supervised_user_id);
169  std::string device_name;
170  EXPECT_TRUE(GetValueForKey(upload_data, "device_name", &device_name));
171  EXPECT_EQ(kDeviceName, device_name);
172  std::string device_id;
173  EXPECT_EQ(!device_id_.empty(),
174            GetValueForKey(upload_data, "device_id", &device_id));
175  EXPECT_EQ(device_id_, device_id);
176  return url_fetcher;
177}
178
179net::TestURLFetcher*
180SupervisedUserRefreshTokenFetcherTest::GetRefreshTokenRequest() {
181  net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(
182      gaia::GaiaOAuthClient::kUrlFetcherId);
183  if (!url_fetcher)
184    return NULL;
185
186  EXPECT_EQ(GaiaUrls::GetInstance()->oauth2_token_url(),
187            url_fetcher->GetOriginalURL());
188  std::string auth_code;
189  EXPECT_TRUE(GetValueForKey(url_fetcher->upload_data(), "code", &auth_code));
190  EXPECT_EQ(kAuthorizationCode, auth_code);
191  return url_fetcher;
192}
193
194void
195SupervisedUserRefreshTokenFetcherTest::MakeOAuth2TokenServiceRequestSucceed() {
196  std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests =
197      oauth2_token_service_.GetPendingRequests();
198  VerifyTokenRequest(requests);
199  base::Time expiration_date = base::Time::Now() +
200                               base::TimeDelta::FromHours(1);
201  oauth2_token_service_.IssueTokenForScope(requests[0].scopes,
202                                           kAccessToken,
203                                           expiration_date);
204}
205
206void
207SupervisedUserRefreshTokenFetcherTest::MakeOAuth2TokenServiceRequestFail(
208    GoogleServiceAuthError::State error) {
209  std::vector<FakeProfileOAuth2TokenService::PendingRequest> requests =
210      oauth2_token_service_.GetPendingRequests();
211  VerifyTokenRequest(requests);
212  oauth2_token_service_.IssueErrorForScope(requests[0].scopes,
213                                           GoogleServiceAuthError(error));
214}
215
216void SupervisedUserRefreshTokenFetcherTest::MakeIssueTokenRequestSucceed() {
217  SendResponse(GetIssueTokenRequest(),
218               base::StringPrintf(kIssueTokenResponseFormat,
219                                  kAuthorizationCode));
220}
221
222void SupervisedUserRefreshTokenFetcherTest::MakeRefreshTokenFetchSucceed() {
223  SendResponse(GetRefreshTokenRequest(),
224               base::StringPrintf(kGetRefreshTokenResponseFormat,
225                                  kSupervisedUserToken));
226}
227
228void SupervisedUserRefreshTokenFetcherTest::Reset() {
229  token_fetcher_.reset();
230}
231
232void SupervisedUserRefreshTokenFetcherTest::OnTokenFetched(
233    const GoogleServiceAuthError& error,
234    const std::string& token) {
235  error_ = error;
236  token_ = token;
237}
238
239// Tests --------------------------------------------------------
240
241TEST_F(SupervisedUserRefreshTokenFetcherTest, Success) {
242  StartFetching();
243  MakeOAuth2TokenServiceRequestSucceed();
244  MakeIssueTokenRequestSucceed();
245  MakeRefreshTokenFetchSucceed();
246
247  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
248  EXPECT_EQ(kSupervisedUserToken, token());
249}
250
251TEST_F(SupervisedUserRefreshTokenFetcherTest, Success_WithDeviceId) {
252  StartFetchingWithDeviceId(kDeviceId);
253  MakeOAuth2TokenServiceRequestSucceed();
254  MakeIssueTokenRequestSucceed();
255  MakeRefreshTokenFetchSucceed();
256
257  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
258  EXPECT_EQ(kSupervisedUserToken, token());
259}
260
261TEST_F(SupervisedUserRefreshTokenFetcherTest, ExpiredAccessToken) {
262  StartFetching();
263  MakeOAuth2TokenServiceRequestSucceed();
264  SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
265  MakeOAuth2TokenServiceRequestSucceed();
266  MakeIssueTokenRequestSucceed();
267  MakeRefreshTokenFetchSucceed();
268
269  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
270  EXPECT_EQ(kSupervisedUserToken, token());
271}
272
273TEST_F(SupervisedUserRefreshTokenFetcherTest, ExpiredAccessTokenRetry) {
274  // If we get a 401 error for the second time, we should give up instead of
275  // retrying again.
276  StartFetching();
277  MakeOAuth2TokenServiceRequestSucceed();
278  SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
279  MakeOAuth2TokenServiceRequestSucceed();
280  SetHttpError(GetIssueTokenRequest(), net::HTTP_UNAUTHORIZED);
281
282  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
283  EXPECT_EQ(net::ERR_FAILED, error().network_error());
284  EXPECT_EQ(std::string(), token());
285}
286
287TEST_F(SupervisedUserRefreshTokenFetcherTest, MalformedIssueTokenResponse) {
288  StartFetching();
289  MakeOAuth2TokenServiceRequestSucceed();
290  SendResponse(GetIssueTokenRequest(), "choke");
291
292  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
293  EXPECT_EQ(net::ERR_INVALID_RESPONSE, error().network_error());
294  EXPECT_EQ(std::string(), token());
295}
296
297TEST_F(SupervisedUserRefreshTokenFetcherTest, FetchAccessTokenFailure) {
298  StartFetching();
299  MakeOAuth2TokenServiceRequestFail(
300      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
301
302  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error().state());
303  EXPECT_EQ(std::string(), token());
304}
305
306TEST_F(SupervisedUserRefreshTokenFetcherTest, IssueTokenNetworkError) {
307  StartFetching();
308  MakeOAuth2TokenServiceRequestSucceed();
309  SetNetworkError(GetIssueTokenRequest(), net::ERR_SSL_PROTOCOL_ERROR);
310
311  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
312  EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, error().network_error());
313  EXPECT_EQ(std::string(), token());
314}
315
316TEST_F(SupervisedUserRefreshTokenFetcherTest, FetchRefreshTokenNetworkError) {
317  StartFetching();
318  MakeOAuth2TokenServiceRequestSucceed();
319  MakeIssueTokenRequestSucceed();
320  SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
321  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
322  SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
323
324  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
325  EXPECT_EQ(net::ERR_FAILED, error().network_error());
326  EXPECT_EQ(std::string(), token());
327}
328
329TEST_F(SupervisedUserRefreshTokenFetcherTest,
330       FetchRefreshTokenTransientNetworkError) {
331  StartFetching();
332  MakeOAuth2TokenServiceRequestSucceed();
333  MakeIssueTokenRequestSucceed();
334  SetNetworkError(GetRefreshTokenRequest(), net::ERR_CONNECTION_REFUSED);
335
336  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
337  MakeRefreshTokenFetchSucceed();
338
339  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
340  EXPECT_EQ(kSupervisedUserToken, token());
341}
342
343TEST_F(SupervisedUserRefreshTokenFetcherTest, FetchRefreshTokenBadRequest) {
344  StartFetching();
345  MakeOAuth2TokenServiceRequestSucceed();
346  MakeIssueTokenRequestSucceed();
347  SetHttpError(GetRefreshTokenRequest(), net::HTTP_BAD_REQUEST);
348
349  EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error().state());
350  EXPECT_EQ(net::ERR_FAILED, error().network_error());
351  EXPECT_EQ(std::string(), token());
352}
353
354TEST_F(SupervisedUserRefreshTokenFetcherTest, CancelWhileFetchingAccessToken) {
355  StartFetching();
356  Reset();
357
358  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
359  EXPECT_EQ(std::string(), token());
360}
361
362TEST_F(SupervisedUserRefreshTokenFetcherTest, CancelWhileCallingIssueToken) {
363  StartFetching();
364  MakeOAuth2TokenServiceRequestSucceed();
365  Reset();
366
367  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
368  EXPECT_EQ(std::string(), token());
369}
370
371TEST_F(SupervisedUserRefreshTokenFetcherTest, CancelWhileFetchingRefreshToken) {
372  StartFetching();
373  MakeOAuth2TokenServiceRequestSucceed();
374  MakeIssueTokenRequestSucceed();
375  Reset();
376
377  EXPECT_EQ(GoogleServiceAuthError::NONE, error().state());
378  EXPECT_EQ(std::string(), token());
379}
380