Home | History | Annotate | Download | only in integration
      1 // Copyright 2013 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/strings/stringprintf.h"
      6 #include "base/threading/platform_thread.h"
      7 #include "base/time/time.h"
      8 #include "chrome/browser/signin/profile_oauth2_token_service.h"
      9 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     10 #include "chrome/browser/sync/profile_sync_service.h"
     11 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
     12 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
     13 #include "chrome/browser/sync/test/integration/sync_test.h"
     14 #include "google_apis/gaia/google_service_auth_error.h"
     15 #include "net/http/http_status_code.h"
     16 #include "net/url_request/url_request_status.h"
     17 
     18 using bookmarks_helper::AddURL;
     19 
     20 const char kShortLivedOAuth2Token[] =
     21     "{"
     22     "  \"refresh_token\": \"short_lived_refresh_token\","
     23     "  \"access_token\": \"short_lived_access_token\","
     24     "  \"expires_in\": 5,"  // 5 seconds.
     25     "  \"token_type\": \"Bearer\""
     26     "}";
     27 
     28 const char kValidOAuth2Token[] = "{"
     29                                  "  \"refresh_token\": \"new_refresh_token\","
     30                                  "  \"access_token\": \"new_access_token\","
     31                                  "  \"expires_in\": 3600,"  // 1 hour.
     32                                  "  \"token_type\": \"Bearer\""
     33                                  "}";
     34 
     35 const char kInvalidGrantOAuth2Token[] = "{"
     36                                         "  \"error\": \"invalid_grant\""
     37                                         "}";
     38 
     39 const char kInvalidClientOAuth2Token[] = "{"
     40                                          "  \"error\": \"invalid_client\""
     41                                          "}";
     42 
     43 const char kEmptyOAuth2Token[] = "";
     44 
     45 const char kMalformedOAuth2Token[] = "{ \"foo\": ";
     46 
     47 class SyncAuthTest : public SyncTest {
     48  public:
     49   SyncAuthTest() : SyncTest(SINGLE_CLIENT) {}
     50   virtual ~SyncAuthTest() {}
     51 
     52   // Helper function that adds a bookmark with index |bookmark_index| and waits
     53   // for sync to complete. The return value indicates whether the sync cycle
     54   // completed successfully (true) or encountered an auth error (false).
     55   bool AddBookmarkAndWaitForSync(int bookmark_index) {
     56     std::wstring title = base::StringPrintf(L"Bookmark %d", bookmark_index);
     57     GURL url = GURL(base::StringPrintf("http://www.foo%d.com", bookmark_index));
     58     EXPECT_TRUE(AddURL(0, title, url) != NULL);
     59     return GetClient(0)->AwaitFullSyncCompletion();
     60   }
     61 
     62   // Sets the authenticated state of the python sync server to |auth_state| and
     63   // sets the canned response that will be returned to the OAuth2TokenService
     64   // when it tries to fetch an access token.
     65   void SetAuthStateAndTokenResponse(PythonServerAuthState auth_state,
     66                                     const std::string& response_data,
     67                                     net::HttpStatusCode response_code,
     68                                     net::URLRequestStatus::Status status) {
     69     TriggerAuthState(auth_state);
     70 
     71     // If ProfileSyncService observes a transient error like SERVICE_UNAVAILABLE
     72     // or CONNECTION_FAILED, this means the OAuth2TokenService has given up
     73     // trying to reach Gaia. In practice, OA2TS retries a fixed number of times,
     74     // but the count is transparent to PSS.
     75     // Override the max retry count in TokenService so that we instantly trigger
     76     // the case where ProfileSyncService must pick up where OAuth2TokenService
     77     // left off (in terms of retries).
     78     ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile(0))->
     79         set_max_authorization_token_fetch_retries_for_testing(0);
     80 
     81     SetOAuth2TokenResponse(response_data, response_code, status);
     82   }
     83 
     84  private:
     85   DISALLOW_COPY_AND_ASSIGN(SyncAuthTest);
     86 };
     87 
     88 // Verify that sync works with a valid OAuth2 token.
     89 IN_PROC_BROWSER_TEST_F(SyncAuthTest, Sanity) {
     90   ASSERT_TRUE(SetupSync());
     91   SetAuthStateAndTokenResponse(AUTHENTICATED_TRUE,
     92                                kValidOAuth2Token,
     93                                net::HTTP_OK,
     94                                net::URLRequestStatus::SUCCESS);
     95   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
     96 }
     97 
     98 // Verify that ProfileSyncService continues trying to fetch access tokens
     99 // when OAuth2TokenService has encountered more than a fixed number of
    100 // HTTP_INTERNAL_SERVER_ERROR (500) errors.
    101 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnInternalServerError500) {
    102   ASSERT_TRUE(SetupSync());
    103   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    104   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    105                                kValidOAuth2Token,
    106                                net::HTTP_INTERNAL_SERVER_ERROR,
    107                                net::URLRequestStatus::SUCCESS);
    108   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    109   ASSERT_TRUE(
    110       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    111 }
    112 
    113 // Verify that ProfileSyncService continues trying to fetch access tokens
    114 // when OAuth2TokenService has encountered more than a fixed number of
    115 // HTTP_FORBIDDEN (403) errors.
    116 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnHttpForbidden403) {
    117   ASSERT_TRUE(SetupSync());
    118   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    119   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    120                                kEmptyOAuth2Token,
    121                                net::HTTP_FORBIDDEN,
    122                                net::URLRequestStatus::SUCCESS);
    123   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    124   ASSERT_TRUE(
    125       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    126 }
    127 
    128 // Verify that ProfileSyncService continues trying to fetch access tokens
    129 // when OAuth2TokenService has encountered a URLRequestStatus of FAILED.
    130 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnRequestFailed) {
    131   ASSERT_TRUE(SetupSync());
    132   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    133   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    134                                kEmptyOAuth2Token,
    135                                net::HTTP_INTERNAL_SERVER_ERROR,
    136                                net::URLRequestStatus::FAILED);
    137   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    138   ASSERT_TRUE(
    139       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    140 }
    141 
    142 // Verify that ProfileSyncService continues trying to fetch access tokens
    143 // when OAuth2TokenService receives a malformed token.
    144 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryOnMalformedToken) {
    145   ASSERT_TRUE(SetupSync());
    146   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    147   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    148                                kMalformedOAuth2Token,
    149                                net::HTTP_OK,
    150                                net::URLRequestStatus::SUCCESS);
    151   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    152   ASSERT_TRUE(
    153       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    154 }
    155 
    156 // Verify that ProfileSyncService ends up with an INVALID_GAIA_CREDENTIALS auth
    157 // error when an invalid_grant error is returned by OAuth2TokenService with an
    158 // HTTP_BAD_REQUEST (400) response code.
    159 IN_PROC_BROWSER_TEST_F(SyncAuthTest, InvalidGrant) {
    160   ASSERT_TRUE(SetupSync());
    161   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    162   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    163                                kInvalidGrantOAuth2Token,
    164                                net::HTTP_BAD_REQUEST,
    165                                net::URLRequestStatus::SUCCESS);
    166   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    167   ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
    168             GetClient(0)->service()->GetAuthError().state());
    169 }
    170 
    171 // Verify that ProfileSyncService ends up with an SERVICE_ERROR auth error when
    172 // an invalid_client error is returned by OAuth2TokenService with an
    173 // HTTP_BAD_REQUEST (400) response code.
    174 IN_PROC_BROWSER_TEST_F(SyncAuthTest, InvalidClient) {
    175   ASSERT_TRUE(SetupSync());
    176   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    177   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    178                                kInvalidClientOAuth2Token,
    179                                net::HTTP_BAD_REQUEST,
    180                                net::URLRequestStatus::SUCCESS);
    181   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    182   ASSERT_EQ(GoogleServiceAuthError::SERVICE_ERROR,
    183             GetClient(0)->service()->GetAuthError().state());
    184 }
    185 
    186 // Verify that ProfileSyncService ends up with a REQUEST_CANCELED auth error
    187 // when when OAuth2TokenService has encountered a URLRequestStatus of CANCELED.
    188 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RequestCanceled) {
    189   ASSERT_TRUE(SetupSync());
    190   ASSERT_TRUE(AddBookmarkAndWaitForSync(1));
    191   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    192                                kEmptyOAuth2Token,
    193                                net::HTTP_INTERNAL_SERVER_ERROR,
    194                                net::URLRequestStatus::CANCELED);
    195   ASSERT_FALSE(AddBookmarkAndWaitForSync(2));
    196   ASSERT_EQ(GoogleServiceAuthError::REQUEST_CANCELED,
    197             GetClient(0)->service()->GetAuthError().state());
    198 }
    199 
    200 // Verify that ProfileSyncService fails initial sync setup during backend
    201 // initialization and ends up with an INVALID_GAIA_CREDENTIALS auth error when
    202 // an invalid_grant error is returned by OAuth2TokenService with an
    203 // HTTP_BAD_REQUEST (400) response code.
    204 IN_PROC_BROWSER_TEST_F(SyncAuthTest, FailInitialSetupWithPersistentError) {
    205   ASSERT_TRUE(SetupClients());
    206   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    207                                kInvalidGrantOAuth2Token,
    208                                net::HTTP_BAD_REQUEST,
    209                                net::URLRequestStatus::SUCCESS);
    210   ASSERT_FALSE(GetClient(0)->SetupSync());
    211   ASSERT_FALSE(GetClient(0)->service()->sync_initialized());
    212   ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
    213             GetClient(0)->service()->GetAuthError().state());
    214 }
    215 
    216 // Verify that ProfileSyncService fails initial sync setup during backend
    217 // initialization, but continues trying to fetch access tokens when
    218 // OAuth2TokenService receives an HTTP_INTERNAL_SERVER_ERROR (500) response
    219 // code.
    220 IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryInitialSetupWithTransientError) {
    221   ASSERT_TRUE(SetupClients());
    222   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    223                                kEmptyOAuth2Token,
    224                                net::HTTP_INTERNAL_SERVER_ERROR,
    225                                net::URLRequestStatus::SUCCESS);
    226   ASSERT_FALSE(GetClient(0)->SetupSync());
    227   ASSERT_FALSE(GetClient(0)->service()->sync_initialized());
    228   ASSERT_TRUE(
    229       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    230 }
    231 
    232 // Verify that ProfileSyncService fetches a new token when an old token expires.
    233 IN_PROC_BROWSER_TEST_F(SyncAuthTest, TokenExpiry) {
    234   // Initial sync succeeds with a short lived OAuth2 Token.
    235   ASSERT_TRUE(SetupClients());
    236   SetAuthStateAndTokenResponse(AUTHENTICATED_TRUE,
    237                                kShortLivedOAuth2Token,
    238                                net::HTTP_OK,
    239                                net::URLRequestStatus::SUCCESS);
    240   ASSERT_TRUE(GetClient(0)->SetupSync());
    241   std::string old_token = GetClient(0)->service()->GetAccessTokenForTest();
    242 
    243   // Wait until the token has expired.
    244   base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
    245 
    246   // Trigger an auth error on the server so PSS requests OA2TS for a new token
    247   // during the next sync cycle.
    248   SetAuthStateAndTokenResponse(AUTHENTICATED_FALSE,
    249                                kEmptyOAuth2Token,
    250                                net::HTTP_INTERNAL_SERVER_ERROR,
    251                                net::URLRequestStatus::SUCCESS);
    252   ASSERT_FALSE(AddBookmarkAndWaitForSync(1));
    253   ASSERT_TRUE(
    254       GetClient(0)->service()->IsRetryingAccessTokenFetchForTest());
    255 
    256   // Trigger an auth success state and set up a new valid OAuth2 token.
    257   SetAuthStateAndTokenResponse(AUTHENTICATED_TRUE,
    258                                kValidOAuth2Token,
    259                                net::HTTP_OK,
    260                                net::URLRequestStatus::SUCCESS);
    261 
    262   // Verify that the next sync cycle is successful, and uses the new auth token.
    263   ASSERT_TRUE(GetClient(0)->AwaitFullSyncCompletion());
    264   std::string new_token = GetClient(0)->service()->GetAccessTokenForTest();
    265   ASSERT_NE(old_token, new_token);
    266 }
    267