Home | History | Annotate | Download | only in signin
      1 // Copyright (c) 2012 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 "chrome/browser/signin/signin_tracker.h"
      6 
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/profiles/profile.h"
      9 #include "chrome/browser/signin/signin_manager.h"
     10 #include "chrome/browser/signin/signin_manager_factory.h"
     11 #include "chrome/browser/signin/token_service.h"
     12 #include "chrome/browser/signin/token_service_factory.h"
     13 #include "content/public/browser/notification_details.h"
     14 #include "content/public/browser/notification_source.h"
     15 #include "google_apis/gaia/gaia_constants.h"
     16 
     17 static const char* kSignedInServices[] = {
     18   GaiaConstants::kSyncService,
     19   GaiaConstants::kGaiaOAuth2LoginRefreshToken
     20 };
     21 static const int kNumSignedInServices =
     22     arraysize(kSignedInServices);
     23 
     24 // Helper to check if the given token service is relevant for sync.
     25 SigninTracker::SigninTracker(Profile* profile, Observer* observer)
     26     : state_(WAITING_FOR_GAIA_VALIDATION),
     27       profile_(profile),
     28       observer_(observer),
     29       credentials_valid_(false) {
     30   Initialize();
     31 }
     32 
     33 SigninTracker::~SigninTracker() {
     34 }
     35 
     36 void SigninTracker::Initialize() {
     37   DCHECK(observer_);
     38   // Register for notifications from the SigninManager.
     39   registrar_.Add(this,
     40                  chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
     41                  content::Source<Profile>(profile_));
     42   registrar_.Add(this,
     43                  chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
     44                  content::Source<Profile>(profile_));
     45   TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
     46   registrar_.Add(this,
     47                  chrome::NOTIFICATION_TOKEN_AVAILABLE,
     48                  content::Source<TokenService>(token_service));
     49   registrar_.Add(this,
     50                  chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
     51                  content::Source<TokenService>(token_service));
     52 
     53   if (state_ == SERVICES_INITIALIZING)
     54     HandleServiceStateChange();
     55 }
     56 
     57 void SigninTracker::Observe(int type,
     58                             const content::NotificationSource& source,
     59                             const content::NotificationDetails& details) {
     60   // We should not get more than one of these notifications.
     61   switch (type) {
     62     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
     63       DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION);
     64       state_ = SERVICES_INITIALIZING;
     65       // If our services are already signed in, see if it's possible to
     66       // transition to the SIGNIN_COMPLETE state.
     67       HandleServiceStateChange();
     68       break;
     69     case chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED: {
     70       DCHECK_EQ(state_, WAITING_FOR_GAIA_VALIDATION);
     71       const GoogleServiceAuthError& error =
     72           *(content::Details<const GoogleServiceAuthError>(details).ptr());
     73       observer_->SigninFailed(error);
     74       break;
     75     }
     76     case chrome::NOTIFICATION_TOKEN_AVAILABLE:
     77       // A new token is available - check to see if we're all signed in now.
     78       HandleServiceStateChange();
     79       break;
     80     case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED:
     81       if (state_ == SERVICES_INITIALIZING) {
     82         const TokenService::TokenRequestFailedDetails& token_details =
     83             *(content::Details<const TokenService::TokenRequestFailedDetails>(
     84                 details).ptr());
     85         for (int i = 0; i < kNumSignedInServices; ++i) {
     86           if (token_details.service() == kSignedInServices[i]) {
     87             // We got an error loading one of our tokens, so notify our
     88             // observer.
     89             state_ = WAITING_FOR_GAIA_VALIDATION;
     90             observer_->SigninFailed(token_details.error());
     91           }
     92         }
     93       }
     94       break;
     95     default:
     96       NOTREACHED();
     97   }
     98 }
     99 
    100 void SigninTracker::HandleServiceStateChange() {
    101   if (state_ != SERVICES_INITIALIZING) {
    102     // Ignore service updates until after our GAIA credentials are validated.
    103     return;
    104   }
    105 
    106   GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
    107   state_ = GetSigninState(profile_, &error);
    108   switch (state_) {
    109     case WAITING_FOR_GAIA_VALIDATION:
    110       observer_->SigninFailed(error);
    111       break;
    112     case SIGNIN_COMPLETE:
    113       observer_->SigninSuccess();
    114       break;
    115     default:
    116       // State has not changed, nothing to do.
    117       DCHECK_EQ(state_, SERVICES_INITIALIZING);
    118       break;
    119   }
    120 }
    121 
    122 // static
    123 bool SigninTracker::AreServiceTokensLoaded(Profile* profile) {
    124   // See if we have all of the tokens required.
    125   TokenService* token_service = TokenServiceFactory::GetForProfile(profile);
    126   for (int i = 0; i < kNumSignedInServices; ++i) {
    127     if (!token_service->HasTokenForService(kSignedInServices[i])) {
    128       // Don't have a token for one of our signed-in services.
    129       return false;
    130     }
    131   }
    132   return true;
    133 }
    134 
    135 // static
    136 SigninTracker::LoginState SigninTracker::GetSigninState(
    137     Profile* profile,
    138     GoogleServiceAuthError* error) {
    139   SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
    140   if (signin->GetAuthenticatedUsername().empty()) {
    141     // User is signed out, trigger a signin failure.
    142     if (error)
    143       *error = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
    144     return WAITING_FOR_GAIA_VALIDATION;
    145   }
    146 
    147   // If we haven't loaded all our service tokens yet, just exit (we'll be called
    148   // again when another token is loaded, or will transition to SigninFailed if
    149   // the loading fails).
    150   if (!AreServiceTokensLoaded(profile))
    151     return SERVICES_INITIALIZING;
    152 
    153   return SIGNIN_COMPLETE;
    154 }
    155